166. Araxis Merge File Comparison Report

Produced by Araxis Merge on 2023-07-12 04:42:42 +0000. See www.araxis.com for information about Merge. This report uses XHTML and CSS2, and is best viewed with a modern standards-compliant browser. For optimum results when printing this report, use landscape orientation and enable printing of background images and colours in your browser.

166.1 Files compared

#LocationFileLast Modified
12023-07-12 04:42:42 +0000
2Porto v4.0.6/Theme Files/Porto Theme/app/code/Smartwave/Filterproducts/view/base/web/jspackery.pkgd.js2023-07-12 03:45:18 +0000

166.2 Comparison summary

DescriptionBetween
Files 1 and 2
Text BlocksLines
Unchanged00
Changed00
Inserted14089
Removed00

166.3 Comparison options

WhitespaceConsecutive whitespace is treated as a single space
Character caseDifferences in character case are significant
Line endingsDifferences in line endings (CR and LF characters) are ignored
CR/LF charactersNot shown in the comparison detail
Active line-pairing rules

166.4 Active regular expressions

No regular expressions were active.

166.5 Comparison detail

    1 /*!
    2  * Packery PACKAGED v1.4.3
    3  * bin-packing layout library
    4  *
    5  * Licensed GPLv3 for open source use
    6  * or Flickity Commercial License for commercial use
    7  *
    8  * http://packery.metafizzy.co
    9  * Copyright 2015 Metafizzy
    10  */
    11 
    12 /**
    13  * Bridget makes jQuery widgets
    14  * v1.1.0
    15  * MIT license
    16  */
    17 
    18 ( function( window ) {
    19 
    20 
    21 
    22 // -------------------------- utils -------------------------- //
    23 
    24 var slice = Array.prototype.slice;
    25 
    26 function noop() {}
    27 
    28 // -------------------------- definition -------------------------- //
    29 
    30 function defineBridget( $ ) {
    31 
    32 // bail if no jQuery
    33 if ( !$ ) {
    34   return;
    35 }
    36 
    37 // -------------------------- addOptionMethod -------------------------- //
    38 
    39 /**
    40  * adds option method -> $().plugin('option', {...})
    41  * @param {Function} PluginClass - constructor class
    42  */
    43 function addOptionMethod( PluginClass ) {
    44   // don't overwrite original option method
    45   if ( PluginClass.prototype.option ) {
    46     return;
    47   }
    48 
    49   // option setter
    50   PluginClass.prototype.option = function( opts ) {
    51     // bail out if not an object
    52     if ( !$.isPlainObject( opts ) ){
    53       return;
    54     }
    55     this.options = $.extend( true, this.options, opts );
    56   };
    57 }
    58 
    59 // -------------------------- plugin bridge -------------------------- //
    60 
    61 // helper function for logging errors
    62 // $.error breaks jQuery chaining
    63 var logError = typeof console === 'undefined' ? noop :
    64   function( message ) {
    65     console.error( message );
    66   };
    67 
    68 /**
    69  * jQuery plugin bridge, access methods like $elem.plugin('method')
    70  * @param {String} namespace - plugin name
    71  * @param {Function} PluginClass - constructor class
    72  */
    73 function bridge( namespace, PluginClass ) {
    74   // add to jQuery fn namespace
    75   $.fn[ namespace ] = function( options ) {
    76     if ( typeof options === 'string' ) {
    77       // call plugin method when first argument is a string
    78       // get arguments for method
    79       var args = slice.call( arguments, 1 );
    80 
    81       for ( var i=0, len = this.length; i < len; i++ ) {
    82         var elem = this[i];
    83         var instance = $.data( elem, namespace );
    84         if ( !instance ) {
    85           logError( "cannot call methods on " + namespace + " prior to initialization; " +
    86             "attempted to call '" + options + "'" );
    87           continue;
    88         }
    89         if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
    90           logError( "no such method '" + options + "' for " + namespace + " instance" );
    91           continue;
    92         }
    93 
    94         // trigger method with arguments
    95         var returnValue = instance[ options ].apply( instance, args );
    96 
    97         // break look and return first value if provided
    98         if ( returnValue !== undefined ) {
    99           return returnValue;
    100         }
    101       }
    102       // return this if no return value
    103       return this;
    104     } else {
    105       return this.each( function() {
    106         var instance = $.data( this, namespace );
    107         if ( instance ) {
    108           // apply options & init
    109           instance.option( options );
    110           instance._init();
    111         } else {
    112           // initialize new instance
    113           instance = new PluginClass( this, options );
    114           $.data( this, namespace, instance );
    115         }
    116       });
    117     }
    118   };
    119 
    120 }
    121 
    122 // -------------------------- bridget -------------------------- //
    123 
    124 /**
    125  * converts a Prototypical class into a proper jQuery plugin
    126  *   the class must have a ._init method
    127  * @param {String} namespace - plugin name, used in $().pluginName
    128  * @param {Function} PluginClass - constructor class
    129  */
    130 $.bridget = function( namespace, PluginClass ) {
    131   addOptionMethod( PluginClass );
    132   bridge( namespace, PluginClass );
    133 };
    134 
    135 return $.bridget;
    136 
    137 }
    138 
    139 // transport
    140 if ( typeof define === 'function' && define.amd ) {
    141   // AMD
    142   define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget );
    143 } else if ( typeof exports === 'object' ) {
    144   defineBridget( require('jquery') );
    145 } else {
    146   // get jquery from browser global
    147   defineBridget( window.jQuery );
    148 }
    149 defineBridget( window.jQuery );
    150 })( window );
    151 
    152 /*!
    153  * classie v1.0.1
    154  * class helper functions
    155  * from bonzo https://github.com/ded/bonzo
    156  * MIT license
    157  * 
    158  * classie.has( elem, 'my-class' ) -> true/false
    159  * classie.add( elem, 'my-new-class' )
    160  * classie.remove( elem, 'my-unwanted-class' )
    161  * classie.toggle( elem, 'my-class' )
    162  */
    163 
    164 /*jshint browser: true, strict: true, undef: true, unused: true */
    165 /*global define: false, module: false */
    166 
    167 ( function( window ) {
    168 
    169 
    170 
    171 // class helper functions from bonzo https://github.com/ded/bonzo
    172 
    173 function classReg( className ) {
    174   return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
    175 }
    176 
    177 // classList support for class management
    178 // altho to be fair, the api sucks because it won't accept multiple classes at once
    179 var hasClass, addClass, removeClass;
    180 
    181 if ( 'classList' in document.documentElement ) {
    182   hasClass = function( elem, c ) {
    183     return elem.classList.contains( c );
    184   };
    185   addClass = function( elem, c ) {
    186     elem.classList.add( c );
    187   };
    188   removeClass = function( elem, c ) {
    189     elem.classList.remove( c );
    190   };
    191 }
    192 else {
    193   hasClass = function( elem, c ) {
    194     return classReg( c ).test( elem.className );
    195   };
    196   addClass = function( elem, c ) {
    197     if ( !hasClass( elem, c ) ) {
    198       elem.className = elem.className + ' ' + c;
    199     }
    200   };
    201   removeClass = function( elem, c ) {
    202     elem.className = elem.className.replace( classReg( c ), ' ' );
    203   };
    204 }
    205 
    206 function toggleClass( elem, c ) {
    207   var fn = hasClass( elem, c ) ? removeClass : addClass;
    208   fn( elem, c );
    209 }
    210 
    211 var classie = {
    212   // full names
    213   hasClass: hasClass,
    214   addClass: addClass,
    215   removeClass: removeClass,
    216   toggleClass: toggleClass,
    217   // short names
    218   has: hasClass,
    219   add: addClass,
    220   remove: removeClass,
    221   toggle: toggleClass
    222 };
    223 
    224 // transport
    225 if ( typeof define === 'function' && define.amd ) {
    226   // AMD
    227   define( 'classie/classie',classie );
    228 } else if ( typeof exports === 'object' ) {
    229   // CommonJS
    230   module.exports = classie;
    231 } else {
    232   // browser global
    233   window.classie = classie;
    234 }
    235 
    236 })( window );
    237 
    238 /*!
    239  * getStyleProperty v1.0.4
    240  * original by kangax
    241  * http://perfectionkills.com/feature-testing-css-properties/
    242  * MIT license
    243  */
    244 
    245 /*jshint browser: true, strict: true, undef: true */
    246 /*global define: false, exports: false, module: false */
    247 
    248 ( function( window ) {
    249 
    250 
    251 
    252 var prefixes = 'Webkit Moz ms Ms O'.split(' ');
    253 var docElemStyle = document.documentElement.style;
    254 
    255 function getStyleProperty( propName ) {
    256   if ( !propName ) {
    257     return;
    258   }
    259 
    260   // test standard property first
    261   if ( typeof docElemStyle[ propName ] === 'string' ) {
    262     return propName;
    263   }
    264 
    265   // capitalize
    266   propName = propName.charAt(0).toUpperCase() + propName.slice(1);
    267 
    268   // test vendor specific properties
    269   var prefixed;
    270   for ( var i=0, len = prefixes.length; i < len; i++ ) {
    271     prefixed = prefixes[i] + propName;
    272     if ( typeof docElemStyle[ prefixed ] === 'string' ) {
    273       return prefixed;
    274     }
    275   }
    276 }
    277 
    278 // transport
    279 if ( typeof define === 'function' && define.amd ) {
    280   // AMD
    281   define( 'get-style-property/get-style-property',[],function() {
    282     return getStyleProperty;
    283   });
    284 } else if ( typeof exports === 'object' ) {
    285   // CommonJS for Component
    286   module.exports = getStyleProperty;
    287 } else {
    288   // browser global
    289   window.getStyleProperty = getStyleProperty;
    290 }
    291 
    292 })( window );
    293 
    294 /*!
    295  * getSize v1.2.2
    296  * measure size of elements
    297  * MIT license
    298  */
    299 
    300 /*jshint browser: true, strict: true, undef: true, unused: true */
    301 /*global define: false, exports: false, require: false, module: false, console: false */
    302 
    303 ( function( window, undefined ) {
    304 
    305 
    306 
    307 // -------------------------- helpers -------------------------- //
    308 
    309 // get a number from a string, not a percentage
    310 function getStyleSize( value ) {
    311   var num = parseFloat( value );
    312   // not a percent like '100%', and a number
    313   var isValid = value.indexOf('%') === -1 && !isNaN( num );
    314   return isValid && num;
    315 }
    316 
    317 function noop() {}
    318 
    319 var logError = typeof console === 'undefined' ? noop :
    320   function( message ) {
    321     console.error( message );
    322   };
    323 
    324 // -------------------------- measurements -------------------------- //
    325 
    326 var measurements = [
    327   'paddingLeft',
    328   'paddingRight',
    329   'paddingTop',
    330   'paddingBottom',
    331   'marginLeft',
    332   'marginRight',
    333   'marginTop',
    334   'marginBottom',
    335   'borderLeftWidth',
    336   'borderRightWidth',
    337   'borderTopWidth',
    338   'borderBottomWidth'
    339 ];
    340 
    341 function getZeroSize() {
    342   var size = {
    343     width: 0,
    344     height: 0,
    345     innerWidth: 0,
    346     innerHeight: 0,
    347     outerWidth: 0,
    348     outerHeight: 0
    349   };
    350   for ( var i=0, len = measurements.length; i < len; i++ ) {
    351     var measurement = measurements[i];
    352     size[ measurement ] = 0;
    353   }
    354   return size;
    355 }
    356 
    357 
    358 
    359 function defineGetSize( getStyleProperty ) {
    360 
    361 // -------------------------- setup -------------------------- //
    362 
    363 var isSetup = false;
    364 
    365 var getStyle, boxSizingProp, isBoxSizeOuter;
    366 
    367 /**
    368  * setup vars and functions
    369  * do it on initial getSize(), rather than on script load
    370  * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
    371  */
    372 function setup() {
    373   // setup once
    374   if ( isSetup ) {
    375     return;
    376   }
    377   isSetup = true;
    378 
    379   var getComputedStyle = window.getComputedStyle;
    380   getStyle = ( function() {
    381     var getStyleFn = getComputedStyle ?
    382       function( elem ) {
    383         return getComputedStyle( elem, null );
    384       } :
    385       function( elem ) {
    386         return elem.currentStyle;
    387       };
    388 
    389       return function getStyle( elem ) {
    390         var style = getStyleFn( elem );
    391         if ( !style ) {
    392           logError( 'Style returned ' + style +
    393             '. Are you running this code in a hidden iframe on Firefox? ' +
    394             'See http://bit.ly/getsizebug1' );
    395         }
    396         return style;
    397       };
    398   })();
    399 
    400   // -------------------------- box sizing -------------------------- //
    401 
    402   boxSizingProp = getStyleProperty('boxSizing');
    403 
    404   /**
    405    * WebKit measures the outer-width on style.width on border-box elems
    406    * IE & Firefox measures the inner-width
    407    */
    408   if ( boxSizingProp ) {
    409     var div = document.createElement('div');
    410     div.style.width = '200px';
    411     div.style.padding = '1px 2px 3px 4px';
    412     div.style.borderStyle = 'solid';
    413     div.style.borderWidth = '1px 2px 3px 4px';
    414     div.style[ boxSizingProp ] = 'border-box';
    415 
    416     var body = document.body || document.documentElement;
    417     body.appendChild( div );
    418     var style = getStyle( div );
    419 
    420     isBoxSizeOuter = getStyleSize( style.width ) === 200;
    421     body.removeChild( div );
    422   }
    423 
    424 }
    425 
    426 // -------------------------- getSize -------------------------- //
    427 
    428 function getSize( elem ) {
    429   setup();
    430 
    431   // use querySeletor if elem is string
    432   if ( typeof elem === 'string' ) {
    433     elem = document.querySelector( elem );
    434   }
    435 
    436   // do not proceed on non-objects
    437   if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
    438     return;
    439   }
    440 
    441   var style = getStyle( elem );
    442 
    443   // if hidden, everything is 0
    444   if ( style.display === 'none' ) {
    445     return getZeroSize();
    446   }
    447 
    448   var size = {};
    449   size.width = elem.offsetWidth;
    450   size.height = elem.offsetHeight;
    451 
    452   var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
    453     style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );
    454 
    455   // get all measurements
    456   for ( var i=0, len = measurements.length; i < len; i++ ) {
    457     var measurement = measurements[i];
    458     var value = style[ measurement ];
    459     value = mungeNonPixel( elem, value );
    460     var num = parseFloat( value );
    461     // any 'auto', 'medium' value will be 0
    462     size[ measurement ] = !isNaN( num ) ? num : 0;
    463   }
    464 
    465   var paddingWidth = size.paddingLeft + size.paddingRight;
    466   var paddingHeight = size.paddingTop + size.paddingBottom;
    467   var marginWidth = size.marginLeft + size.marginRight;
    468   var marginHeight = size.marginTop + size.marginBottom;
    469   var borderWidth = size.borderLeftWidth + size.borderRightWidth;
    470   var borderHeight = size.borderTopWidth + size.borderBottomWidth;
    471 
    472   var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
    473 
    474   // overwrite width and height if we can get it from style
    475   var styleWidth = getStyleSize( style.width );
    476   if ( styleWidth !== false ) {
    477     size.width = styleWidth +
    478       // add padding and border unless it's already including it
    479       ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
    480   }
    481 
    482   var styleHeight = getStyleSize( style.height );
    483   if ( styleHeight !== false ) {
    484     size.height = styleHeight +
    485       // add padding and border unless it's already including it
    486       ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
    487   }
    488 
    489   size.innerWidth = size.width - ( paddingWidth + borderWidth );
    490   size.innerHeight = size.height - ( paddingHeight + borderHeight );
    491 
    492   size.outerWidth = size.width + marginWidth;
    493   size.outerHeight = size.height + marginHeight;
    494 
    495   return size;
    496 }
    497 
    498 // IE8 returns percent values, not pixels
    499 // taken from jQuery's curCSS
    500 function mungeNonPixel( elem, value ) {
    501   // IE8 and has percent value
    502   if ( window.getComputedStyle || value.indexOf('%') === -1 ) {
    503     return value;
    504   }
    505   var style = elem.style;
    506   // Remember the original values
    507   var left = style.left;
    508   var rs = elem.runtimeStyle;
    509   var rsLeft = rs && rs.left;
    510 
    511   // Put in the new values to get a computed value out
    512   if ( rsLeft ) {
    513     rs.left = elem.currentStyle.left;
    514   }
    515   style.left = value;
    516   value = style.pixelLeft;
    517 
    518   // Revert the changed values
    519   style.left = left;
    520   if ( rsLeft ) {
    521     rs.left = rsLeft;
    522   }
    523 
    524   return value;
    525 }
    526 
    527 return getSize;
    528 
    529 }
    530 
    531 // transport
    532 if ( typeof define === 'function' && define.amd ) {
    533   // AMD for RequireJS
    534   define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize );
    535 } else if ( typeof exports === 'object' ) {
    536   // CommonJS for Component
    537   module.exports = defineGetSize( require('desandro-get-style-property') );
    538 } else {
    539   // browser global
    540   window.getSize = defineGetSize( window.getStyleProperty );
    541 }
    542 
    543 })( window );
    544 
    545 /*!
    546  * eventie v1.0.6
    547  * event binding helper
    548  *   eventie.bind( elem, 'click', myFn )
    549  *   eventie.unbind( elem, 'click', myFn )
    550  * MIT license
    551  */
    552 
    553 /*jshint browser: true, undef: true, unused: true */
    554 /*global define: false, module: false */
    555 
    556 ( function( window ) {
    557 
    558 
    559 
    560 var docElem = document.documentElement;
    561 
    562 var bind = function() {};
    563 
    564 function getIEEvent( obj ) {
    565   var event = window.event;
    566   // add event.target
    567   event.target = event.target || event.srcElement || obj;
    568   return event;
    569 }
    570 
    571 if ( docElem.addEventListener ) {
    572   bind = function( obj, type, fn ) {
    573     obj.addEventListener( type, fn, false );
    574   };
    575 } else if ( docElem.attachEvent ) {
    576   bind = function( obj, type, fn ) {
    577     obj[ type + fn ] = fn.handleEvent ?
    578       function() {
    579         var event = getIEEvent( obj );
    580         fn.handleEvent.call( fn, event );
    581       } :
    582       function() {
    583         var event = getIEEvent( obj );
    584         fn.call( obj, event );
    585       };
    586     obj.attachEvent( "on" + type, obj[ type + fn ] );
    587   };
    588 }
    589 
    590 var unbind = function() {};
    591 
    592 if ( docElem.removeEventListener ) {
    593   unbind = function( obj, type, fn ) {
    594     obj.removeEventListener( type, fn, false );
    595   };
    596 } else if ( docElem.detachEvent ) {
    597   unbind = function( obj, type, fn ) {
    598     obj.detachEvent( "on" + type, obj[ type + fn ] );
    599     try {
    600       delete obj[ type + fn ];
    601     } catch ( err ) {
    602       // can't delete window object properties
    603       obj[ type + fn ] = undefined;
    604     }
    605   };
    606 }
    607 
    608 var eventie = {
    609   bind: bind,
    610   unbind: unbind
    611 };
    612 
    613 // ----- module definition ----- //
    614 
    615 if ( typeof define === 'function' && define.amd ) {
    616   // AMD
    617   define( 'eventie/eventie',eventie );
    618 } else if ( typeof exports === 'object' ) {
    619   // CommonJS
    620   module.exports = eventie;
    621 } else {
    622   // browser global
    623   window.eventie = eventie;
    624 }
    625 window.eventie = eventie;
    626 })( window );
    627 
    628 /*!
    629  * EventEmitter v4.2.11 - git.io/ee
    630  * Unlicense - http://unlicense.org/
    631  * Oliver Caldwell - http://oli.me.uk/
    632  * @preserve
    633  */
    634 
    635 ;(function () {
    636     
    637 
    638     /**
    639      * Class for managing events.
    640      * Can be extended to provide event functionality in other classes.
    641      *
    642      * @class EventEmitter Manages event registering and emitting.
    643      */
    644     function EventEmitter() {}
    645 
    646     // Shortcuts to improve speed and size
    647     var proto = EventEmitter.prototype;
    648     var exports = this;
    649     var originalGlobalValue = exports.EventEmitter;
    650 
    651     /**
    652      * Finds the index of the listener for the event in its storage array.
    653      *
    654      * @param {Function[]} listeners Array of listeners to search through.
    655      * @param {Function} listener Method to look for.
    656      * @return {Number} Index of the specified listener, -1 if not found
    657      * @api private
    658      */
    659     function indexOfListener(listeners, listener) {
    660         var i = listeners.length;
    661         while (i--) {
    662             if (listeners[i].listener === listener) {
    663                 return i;
    664             }
    665         }
    666 
    667         return -1;
    668     }
    669 
    670     /**
    671      * Alias a method while keeping the context correct, to allow for overwriting of target method.
    672      *
    673      * @param {String} name The name of the target method.
    674      * @return {Function} The aliased method
    675      * @api private
    676      */
    677     function alias(name) {
    678         return function aliasClosure() {
    679             return this[name].apply(this, arguments);
    680         };
    681     }
    682 
    683     /**
    684      * Returns the listener array for the specified event.
    685      * Will initialise the event object and listener arrays if required.
    686      * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
    687      * Each property in the object response is an array of listener functions.
    688      *
    689      * @param {String|RegExp} evt Name of the event to return the listeners from.
    690      * @return {Function[]|Object} All listener functions for the event.
    691      */
    692     proto.getListeners = function getListeners(evt) {
    693         var events = this._getEvents();
    694         var response;
    695         var key;
    696 
    697         // Return a concatenated array of all matching events if
    698         // the selector is a regular expression.
    699         if (evt instanceof RegExp) {
    700             response = {};
    701             for (key in events) {
    702                 if (events.hasOwnProperty(key) && evt.test(key)) {
    703                     response[key] = events[key];
    704                 }
    705             }
    706         }
    707         else {
    708             response = events[evt] || (events[evt] = []);
    709         }
    710 
    711         return response;
    712     };
    713 
    714     /**
    715      * Takes a list of listener objects and flattens it into a list of listener functions.
    716      *
    717      * @param {Object[]} listeners Raw listener objects.
    718      * @return {Function[]} Just the listener functions.
    719      */
    720     proto.flattenListeners = function flattenListeners(listeners) {
    721         var flatListeners = [];
    722         var i;
    723 
    724         for (i = 0; i < listeners.length; i += 1) {
    725             flatListeners.push(listeners[i].listener);
    726         }
    727 
    728         return flatListeners;
    729     };
    730 
    731     /**
    732      * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
    733      *
    734      * @param {String|RegExp} evt Name of the event to return the listeners from.
    735      * @return {Object} All listener functions for an event in an object.
    736      */
    737     proto.getListenersAsObject = function getListenersAsObject(evt) {
    738         var listeners = this.getListeners(evt);
    739         var response;
    740 
    741         if (listeners instanceof Array) {
    742             response = {};
    743             response[evt] = listeners;
    744         }
    745 
    746         return response || listeners;
    747     };
    748 
    749     /**
    750      * Adds a listener function to the specified event.
    751      * The listener will not be added if it is a duplicate.
    752      * If the listener returns true then it will be removed after it is called.
    753      * If you pass a regular expression as the event name then the listener will be added to all events that match it.
    754      *
    755      * @param {String|RegExp} evt Name of the event to attach the listener to.
    756      * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
    757      * @return {Object} Current instance of EventEmitter for chaining.
    758      */
    759     proto.addListener = function addListener(evt, listener) {
    760         var listeners = this.getListenersAsObject(evt);
    761         var listenerIsWrapped = typeof listener === 'object';
    762         var key;
    763 
    764         for (key in listeners) {
    765             if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
    766                 listeners[key].push(listenerIsWrapped ? listener : {
    767                     listener: listener,
    768                     once: false
    769                 });
    770             }
    771         }
    772 
    773         return this;
    774     };
    775 
    776     /**
    777      * Alias of addListener
    778      */
    779     proto.on = alias('addListener');
    780 
    781     /**
    782      * Semi-alias of addListener. It will add a listener that will be
    783      * automatically removed after its first execution.
    784      *
    785      * @param {String|RegExp} evt Name of the event to attach the listener to.
    786      * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
    787      * @return {Object} Current instance of EventEmitter for chaining.
    788      */
    789     proto.addOnceListener = function addOnceListener(evt, listener) {
    790         return this.addListener(evt, {
    791             listener: listener,
    792             once: true
    793         });
    794     };
    795 
    796     /**
    797      * Alias of addOnceListener.
    798      */
    799     proto.once = alias('addOnceListener');
    800 
    801     /**
    802      * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
    803      * You need to tell it what event names should be matched by a regex.
    804      *
    805      * @param {String} evt Name of the event to create.
    806      * @return {Object} Current instance of EventEmitter for chaining.
    807      */
    808     proto.defineEvent = function defineEvent(evt) {
    809         this.getListeners(evt);
    810         return this;
    811     };
    812 
    813     /**
    814      * Uses defineEvent to define multiple events.
    815      *
    816      * @param {String[]} evts An array of event names to define.
    817      * @return {Object} Current instance of EventEmitter for chaining.
    818      */
    819     proto.defineEvents = function defineEvents(evts) {
    820         for (var i = 0; i < evts.length; i += 1) {
    821             this.defineEvent(evts[i]);
    822         }
    823         return this;
    824     };
    825 
    826     /**
    827      * Removes a listener function from the specified event.
    828      * When passed a regular expression as the event name, it will remove the listener from all events that match it.
    829      *
    830      * @param {String|RegExp} evt Name of the event to remove the listener from.
    831      * @param {Function} listener Method to remove from the event.
    832      * @return {Object} Current instance of EventEmitter for chaining.
    833      */
    834     proto.removeListener = function removeListener(evt, listener) {
    835         var listeners = this.getListenersAsObject(evt);
    836         var index;
    837         var key;
    838 
    839         for (key in listeners) {
    840             if (listeners.hasOwnProperty(key)) {
    841                 index = indexOfListener(listeners[key], listener);
    842 
    843                 if (index !== -1) {
    844                     listeners[key].splice(index, 1);
    845                 }
    846             }
    847         }
    848 
    849         return this;
    850     };
    851 
    852     /**
    853      * Alias of removeListener
    854      */
    855     proto.off = alias('removeListener');
    856 
    857     /**
    858      * Adds listeners in bulk using the manipulateListeners method.
    859      * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
    860      * You can also pass it a regular expression to add the array of listeners to all events that match it.
    861      * Yeah, this function does quite a bit. That's probably a bad thing.
    862      *
    863      * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
    864      * @param {Function[]} [listeners] An optional array of listener functions to add.
    865      * @return {Object} Current instance of EventEmitter for chaining.
    866      */
    867     proto.addListeners = function addListeners(evt, listeners) {
    868         // Pass through to manipulateListeners
    869         return this.manipulateListeners(false, evt, listeners);
    870     };
    871 
    872     /**
    873      * Removes listeners in bulk using the manipulateListeners method.
    874      * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
    875      * You can also pass it an event name and an array of listeners to be removed.
    876      * You can also pass it a regular expression to remove the listeners from all events that match it.
    877      *
    878      * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
    879      * @param {Function[]} [listeners] An optional array of listener functions to remove.
    880      * @return {Object} Current instance of EventEmitter for chaining.
    881      */
    882     proto.removeListeners = function removeListeners(evt, listeners) {
    883         // Pass through to manipulateListeners
    884         return this.manipulateListeners(true, evt, listeners);
    885     };
    886 
    887     /**
    888      * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
    889      * The first argument will determine if the listeners are removed (true) or added (false).
    890      * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
    891      * You can also pass it an event name and an array of listeners to be added/removed.
    892      * You can also pass it a regular expression to manipulate the listeners of all events that match it.
    893      *
    894      * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
    895      * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
    896      * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
    897      * @return {Object} Current instance of EventEmitter for chaining.
    898      */
    899     proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
    900         var i;
    901         var value;
    902         var single = remove ? this.removeListener : this.addListener;
    903         var multiple = remove ? this.removeListeners : this.addListeners;
    904 
    905         // If evt is an object then pass each of its properties to this method
    906         if (typeof evt === 'object' && !(evt instanceof RegExp)) {
    907             for (i in evt) {
    908                 if (evt.hasOwnProperty(i) && (value = evt[i])) {
    909                     // Pass the single listener straight through to the singular method
    910                     if (typeof value === 'function') {
    911                         single.call(this, i, value);
    912                     }
    913                     else {
    914                         // Otherwise pass back to the multiple function
    915                         multiple.call(this, i, value);
    916                     }
    917                 }
    918             }
    919         }
    920         else {
    921             // So evt must be a string
    922             // And listeners must be an array of listeners
    923             // Loop over it and pass each one to the multiple method
    924             i = listeners.length;
    925             while (i--) {
    926                 single.call(this, evt, listeners[i]);
    927             }
    928         }
    929 
    930         return this;
    931     };
    932 
    933     /**
    934      * Removes all listeners from a specified event.
    935      * If you do not specify an event then all listeners will be removed.
    936      * That means every event will be emptied.
    937      * You can also pass a regex to remove all events that match it.
    938      *
    939      * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
    940      * @return {Object} Current instance of EventEmitter for chaining.
    941      */
    942     proto.removeEvent = function removeEvent(evt) {
    943         var type = typeof evt;
    944         var events = this._getEvents();
    945         var key;
    946 
    947         // Remove different things depending on the state of evt
    948         if (type === 'string') {
    949             // Remove all listeners for the specified event
    950             delete events[evt];
    951         }
    952         else if (evt instanceof RegExp) {
    953             // Remove all events matching the regex.
    954             for (key in events) {
    955                 if (events.hasOwnProperty(key) && evt.test(key)) {
    956                     delete events[key];
    957                 }
    958             }
    959         }
    960         else {
    961             // Remove all listeners in all events
    962             delete this._events;
    963         }
    964 
    965         return this;
    966     };
    967 
    968     /**
    969      * Alias of removeEvent.
    970      *
    971      * Added to mirror the node API.
    972      */
    973     proto.removeAllListeners = alias('removeEvent');
    974 
    975     /**
    976      * Emits an event of your choice.
    977      * When emitted, every listener attached to that event will be executed.
    978      * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
    979      * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
    980      * So they will not arrive within the array on the other side, they will be separate.
    981      * You can also pass a regular expression to emit to all events that match it.
    982      *
    983      * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
    984      * @param {Array} [args] Optional array of arguments to be passed to each listener.
    985      * @return {Object} Current instance of EventEmitter for chaining.
    986      */
    987     proto.emitEvent = function emitEvent(evt, args) {
    988         var listeners = this.getListenersAsObject(evt);
    989         var listener;
    990         var i;
    991         var key;
    992         var response;
    993 
    994         for (key in listeners) {
    995             if (listeners.hasOwnProperty(key)) {
    996                 i = listeners[key].length;
    997 
    998                 while (i--) {
    999                     // If the listener returns true then it shall be removed from the event
    1000                     // The function is executed either with a basic call or an apply if there is an args array
    1001                     listener = listeners[key][i];
    1002 
    1003                     if (listener.once === true) {
    1004                         this.removeListener(evt, listener.listener);
    1005                     }
    1006 
    1007                     response = listener.listener.apply(this, args || []);
    1008 
    1009                     if (response === this._getOnceReturnValue()) {
    1010                         this.removeListener(evt, listener.listener);
    1011                     }
    1012                 }
    1013             }
    1014         }
    1015 
    1016         return this;
    1017     };
    1018 
    1019     /**
    1020      * Alias of emitEvent
    1021      */
    1022     proto.trigger = alias('emitEvent');
    1023 
    1024     /**
    1025      * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
    1026      * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
    1027      *
    1028      * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
    1029      * @param {...*} Optional additional arguments to be passed to each listener.
    1030      * @return {Object} Current instance of EventEmitter for chaining.
    1031      */
    1032     proto.emit = function emit(evt) {
    1033         var args = Array.prototype.slice.call(arguments, 1);
    1034         return this.emitEvent(evt, args);
    1035     };
    1036 
    1037     /**
    1038      * Sets the current value to check against when executing listeners. If a
    1039      * listeners return value matches the one set here then it will be removed
    1040      * after execution. This value defaults to true.
    1041      *
    1042      * @param {*} value The new value to check for when executing listeners.
    1043      * @return {Object} Current instance of EventEmitter for chaining.
    1044      */
    1045     proto.setOnceReturnValue = function setOnceReturnValue(value) {
    1046         this._onceReturnValue = value;
    1047         return this;
    1048     };
    1049 
    1050     /**
    1051      * Fetches the current value to check against when executing listeners. If
    1052      * the listeners return value matches this one then it should be removed
    1053      * automatically. It will return true by default.
    1054      *
    1055      * @return {*|Boolean} The current value to check for or the default, true.
    1056      * @api private
    1057      */
    1058     proto._getOnceReturnValue = function _getOnceReturnValue() {
    1059         if (this.hasOwnProperty('_onceReturnValue')) {
    1060             return this._onceReturnValue;
    1061         }
    1062         else {
    1063             return true;
    1064         }
    1065     };
    1066 
    1067     /**
    1068      * Fetches the events object and creates one if required.
    1069      *
    1070      * @return {Object} The events storage object.
    1071      * @api private
    1072      */
    1073     proto._getEvents = function _getEvents() {
    1074         return this._events || (this._events = {});
    1075     };
    1076 
    1077     /**
    1078      * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
    1079      *
    1080      * @return {Function} Non conflicting EventEmitter class.
    1081      */
    1082     EventEmitter.noConflict = function noConflict() {
    1083         exports.EventEmitter = originalGlobalValue;
    1084         return EventEmitter;
    1085     };
    1086 
    1087     // Expose the class either via AMD, CommonJS or the global object
    1088     if (typeof define === 'function' && define.amd) {
    1089         define('eventEmitter/EventEmitter',[],function () {
    1090             return EventEmitter;
    1091         });
    1092     }
    1093     else if (typeof module === 'object' && module.exports){
    1094         module.exports = EventEmitter;
    1095     }
    1096     else {
    1097         exports.EventEmitter = EventEmitter;
    1098     }
    1099 }.call(this));
    1100 
    1101 /*!
    1102  * docReady v1.0.4
    1103  * Cross browser DOMContentLoaded event emitter
    1104  * MIT license
    1105  */
    1106 
    1107 /*jshint browser: true, strict: true, undef: true, unused: true*/
    1108 /*global define: false, require: false, module: false */
    1109 
    1110 ( function( window ) {
    1111 
    1112 
    1113 
    1114 var document = window.document;
    1115 // collection of functions to be triggered on ready
    1116 var queue = [];
    1117 
    1118 function docReady( fn ) {
    1119   // throw out non-functions
    1120   if ( typeof fn !== 'function' ) {
    1121     return;
    1122   }
    1123 
    1124   if ( docReady.isReady ) {
    1125     // ready now, hit it
    1126     fn();
    1127   } else {
    1128     // queue function when ready
    1129     queue.push( fn );
    1130   }
    1131 }
    1132 
    1133 docReady.isReady = false;
    1134 
    1135 // triggered on various doc ready events
    1136 function onReady( event ) {
    1137   // bail if already triggered or IE8 document is not ready just yet
    1138   var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete';
    1139   if ( docReady.isReady || isIE8NotReady ) {
    1140     return;
    1141   }
    1142 
    1143   trigger();
    1144 }
    1145 
    1146 function trigger() {
    1147   docReady.isReady = true;
    1148   // process queue
    1149   for ( var i=0, len = queue.length; i < len; i++ ) {
    1150     var fn = queue[i];
    1151     fn();
    1152   }
    1153 }
    1154 
    1155 function defineDocReady( eventie ) {
    1156   // trigger ready if page is ready
    1157   if ( document.readyState === 'complete' ) {
    1158     trigger();
    1159   } else {
    1160     // listen for events
    1161     window.eventie.bind( document, 'DOMContentLoaded', onReady );
    1162     window.eventie.bind( document, 'readystatechange', onReady );
    1163     window.eventie.bind( window, 'load', onReady );
    1164   }
    1165 
    1166   return docReady;
    1167 }
    1168 
    1169 // transport
    1170 if ( typeof define === 'function' && define.amd ) {
    1171   // AMD
    1172   define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady );
    1173 } else if ( typeof exports === 'object' ) {
    1174   module.exports = defineDocReady( require('eventie') );
    1175 } else {
    1176   // browser global
    1177   window.docReady = defineDocReady( window.eventie );
    1178 }
    1179 
    1180 })( window );
    1181 
    1182 /**
    1183  * matchesSelector v1.0.3
    1184  * matchesSelector( element, '.selector' )
    1185  * MIT license
    1186  */
    1187 
    1188 /*jshint browser: true, strict: true, undef: true, unused: true */
    1189 /*global define: false, module: false */
    1190 
    1191 ( function( ElemProto ) {
    1192 
    1193   
    1194 
    1195   var matchesMethod = ( function() {
    1196     // check for the standard method name first
    1197     if ( ElemProto.matches ) {
    1198       return 'matches';
    1199     }
    1200     // check un-prefixed
    1201     if ( ElemProto.matchesSelector ) {
    1202       return 'matchesSelector';
    1203     }
    1204     // check vendor prefixes
    1205     var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
    1206 
    1207     for ( var i=0, len = prefixes.length; i < len; i++ ) {
    1208       var prefix = prefixes[i];
    1209       var method = prefix + 'MatchesSelector';
    1210       if ( ElemProto[ method ] ) {
    1211         return method;
    1212       }
    1213     }
    1214   })();
    1215 
    1216   // ----- match ----- //
    1217 
    1218   function match( elem, selector ) {
    1219     return elem[ matchesMethod ]( selector );
    1220   }
    1221 
    1222   // ----- appendToFragment ----- //
    1223 
    1224   function checkParent( elem ) {
    1225     // not needed if already has parent
    1226     if ( elem.parentNode ) {
    1227       return;
    1228     }
    1229     var fragment = document.createDocumentFragment();
    1230     fragment.appendChild( elem );
    1231   }
    1232 
    1233   // ----- query ----- //
    1234 
    1235   // fall back to using QSA
    1236   // thx @jonathantneal https://gist.github.com/3062955
    1237   function query( elem, selector ) {
    1238     // append to fragment if no parent
    1239     checkParent( elem );
    1240 
    1241     // match elem with all selected elems of parent
    1242     var elems = elem.parentNode.querySelectorAll( selector );
    1243     for ( var i=0, len = elems.length; i < len; i++ ) {
    1244       // return true if match
    1245       if ( elems[i] === elem ) {
    1246         return true;
    1247       }
    1248     }
    1249     // otherwise return false
    1250     return false;
    1251   }
    1252 
    1253   // ----- matchChild ----- //
    1254 
    1255   function matchChild( elem, selector ) {
    1256     checkParent( elem );
    1257     return match( elem, selector );
    1258   }
    1259 
    1260   // ----- matchesSelector ----- //
    1261 
    1262   var matchesSelector;
    1263 
    1264   if ( matchesMethod ) {
    1265     // IE9 supports matchesSelector, but doesn't work on orphaned elems
    1266     // check for that
    1267     var div = document.createElement('div');
    1268     var supportsOrphans = match( div, 'div' );
    1269     matchesSelector = supportsOrphans ? match : matchChild;
    1270   } else {
    1271     matchesSelector = query;
    1272   }
    1273 
    1274   // transport
    1275   if ( typeof define === 'function' && define.amd ) {
    1276     // AMD
    1277     define( 'matches-selector/matches-selector',[],function() {
    1278       return matchesSelector;
    1279     });
    1280   } else if ( typeof exports === 'object' ) {
    1281     module.exports = matchesSelector;
    1282   }
    1283   else {
    1284     // browser global
    1285     window.matchesSelector = matchesSelector;
    1286   }
    1287 
    1288 })( Element.prototype );
    1289 
    1290 /**
    1291  * Fizzy UI utils v1.0.1
    1292  * MIT license
    1293  */
    1294 
    1295 /*jshint browser: true, undef: true, unused: true, strict: true */
    1296 
    1297 ( function( window, factory ) {
    1298   /*global define: false, module: false, require: false */
    1299   
    1300   // universal module definition
    1301 
    1302   if ( typeof define == 'function' && define.amd ) {
    1303     // AMD
    1304     define( 'fizzy-ui-utils/utils',[
    1305       'doc-ready/doc-ready',
    1306       'matches-selector/matches-selector'
    1307     ], function( docReady, matchesSelector ) {
    1308       return factory( window, docReady, matchesSelector );
    1309     });
    1310   } else if ( typeof exports == 'object' ) {
    1311     // CommonJS
    1312     module.exports = factory(
    1313       window,
    1314       require('doc-ready'),
    1315       require('desandro-matches-selector')
    1316     );
    1317   } else {
    1318     // browser global
    1319     window.fizzyUIUtils = factory(
    1320       window,
    1321       window.docReady,
    1322       window.matchesSelector
    1323     );
    1324   }
    1325 
    1326 }( window, function factory( window, docReady, matchesSelector ) {
    1327 
    1328 
    1329 
    1330 var utils = {};
    1331 
    1332 // ----- extend ----- //
    1333 
    1334 // extends objects
    1335 utils.extend = function( a, b ) {
    1336   for ( var prop in b ) {
    1337     a[ prop ] = b[ prop ];
    1338   }
    1339   return a;
    1340 };
    1341 
    1342 // ----- modulo ----- //
    1343 
    1344 utils.modulo = function( num, div ) {
    1345   return ( ( num % div ) + div ) % div;
    1346 };
    1347 
    1348 // ----- isArray ----- //
    1349   
    1350 var objToString = Object.prototype.toString;
    1351 utils.isArray = function( obj ) {
    1352   return objToString.call( obj ) == '[object Array]';
    1353 };
    1354 
    1355 // ----- makeArray ----- //
    1356 
    1357 // turn element or nodeList into an array
    1358 utils.makeArray = function( obj ) {
    1359   var ary = [];
    1360   if ( utils.isArray( obj ) ) {
    1361     // use object if already an array
    1362     ary = obj;
    1363   } else if ( obj && typeof obj.length == 'number' ) {
    1364     // convert nodeList to array
    1365     for ( var i=0, len = obj.length; i < len; i++ ) {
    1366       ary.push( obj[i] );
    1367     }
    1368   } else {
    1369     // array of single index
    1370     ary.push( obj );
    1371   }
    1372   return ary;
    1373 };
    1374 
    1375 // ----- indexOf ----- //
    1376 
    1377 // index of helper cause IE8
    1378 utils.indexOf = Array.prototype.indexOf ? function( ary, obj ) {
    1379     return ary.indexOf( obj );
    1380   } : function( ary, obj ) {
    1381     for ( var i=0, len = ary.length; i < len; i++ ) {
    1382       if ( ary[i] === obj ) {
    1383         return i;
    1384       }
    1385     }
    1386     return -1;
    1387   };
    1388 
    1389 // ----- removeFrom ----- //
    1390 
    1391 utils.removeFrom = function( ary, obj ) {
    1392   var index = utils.indexOf( ary, obj );
    1393   if ( index != -1 ) {
    1394     ary.splice( index, 1 );
    1395   }
    1396 };
    1397 
    1398 // ----- isElement ----- //
    1399 
    1400 // http://stackoverflow.com/a/384380/182183
    1401 utils.isElement = ( typeof HTMLElement == 'function' || typeof HTMLElement == 'object' ) ?
    1402   function isElementDOM2( obj ) {
    1403     return obj instanceof HTMLElement;
    1404   } :
    1405   function isElementQuirky( obj ) {
    1406     return obj && typeof obj == 'object' &&
    1407       obj.nodeType == 1 && typeof obj.nodeName == 'string';
    1408   };
    1409 
    1410 // ----- setText ----- //
    1411 
    1412 utils.setText = ( function() {
    1413   var setTextProperty;
    1414   function setText( elem, text ) {
    1415     // only check setTextProperty once
    1416     setTextProperty = setTextProperty || ( document.documentElement.textContent !== undefined ? 'textContent' : 'innerText' );
    1417     elem[ setTextProperty ] = text;
    1418   }
    1419   return setText;
    1420 })();
    1421 
    1422 // ----- getParent ----- //
    1423 
    1424 utils.getParent = function( elem, selector ) {
    1425   while ( elem != document.body ) {
    1426     elem = elem.parentNode;
    1427     if ( matchesSelector( elem, selector ) ) {
    1428       return elem;
    1429     }
    1430   }
    1431 };
    1432 
    1433 // ----- getQueryElement ----- //
    1434 
    1435 // use element as selector string
    1436 utils.getQueryElement = function( elem ) {
    1437   if ( typeof elem == 'string' ) {
    1438     return document.querySelector( elem );
    1439   }
    1440   return elem;
    1441 };
    1442 
    1443 // ----- handleEvent ----- //
    1444 
    1445 // enable .ontype to trigger from .addEventListener( elem, 'type' )
    1446 utils.handleEvent = function( event ) {
    1447   var method = 'on' + event.type;
    1448   if ( this[ method ] ) {
    1449     this[ method ]( event );
    1450   }
    1451 };
    1452 
    1453 // ----- filterFindElements ----- //
    1454 
    1455 utils.filterFindElements = function( elems, selector ) {
    1456   // make array of elems
    1457   elems = utils.makeArray( elems );
    1458   var ffElems = [];
    1459 
    1460   for ( var i=0, len = elems.length; i < len; i++ ) {
    1461     var elem = elems[i];
    1462     // check that elem is an actual element
    1463     if ( !utils.isElement( elem ) ) {
    1464       continue;
    1465     }
    1466     // filter & find items if we have a selector
    1467     if ( selector ) {
    1468       // filter siblings
    1469       if ( matchesSelector( elem, selector ) ) {
    1470         ffElems.push( elem );
    1471       }
    1472       // find children
    1473       var childElems = elem.querySelectorAll( selector );
    1474       // concat childElems to filterFound array
    1475       for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
    1476         ffElems.push( childElems[j] );
    1477       }
    1478     } else {
    1479       ffElems.push( elem );
    1480     }
    1481   }
    1482 
    1483   return ffElems;
    1484 };
    1485 
    1486 // ----- debounceMethod ----- //
    1487 
    1488 utils.debounceMethod = function( _class, methodName, threshold ) {
    1489   // original method
    1490   var method = _class.prototype[ methodName ];
    1491   var timeoutName = methodName + 'Timeout';
    1492 
    1493   _class.prototype[ methodName ] = function() {
    1494     var timeout = this[ timeoutName ];
    1495     if ( timeout ) {
    1496       clearTimeout( timeout );
    1497     }
    1498     var args = arguments;
    1499 
    1500     var _this = this;
    1501     this[ timeoutName ] = setTimeout( function() {
    1502       method.apply( _this, args );
    1503       delete _this[ timeoutName ];
    1504     }, threshold || 100 );
    1505   };
    1506 };
    1507 
    1508 // ----- htmlInit ----- //
    1509 
    1510 // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
    1511 utils.toDashed = function( str ) {
    1512   return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
    1513     return $1 + '-' + $2;
    1514   }).toLowerCase();
    1515 };
    1516 
    1517 var console = window.console;
    1518 /**
    1519  * allow user to initialize classes via .js-namespace class
    1520  * htmlInit( Widget, 'widgetName' )
    1521  * options are parsed from data-namespace-option attribute
    1522  */
    1523 utils.htmlInit = function( WidgetClass, namespace ) {
    1524   docReady( function() {
    1525     var dashedNamespace = utils.toDashed( namespace );
    1526     var elems = document.querySelectorAll( '.js-' + dashedNamespace );
    1527     var dataAttr = 'data-' + dashedNamespace + '-options';
    1528 
    1529     for ( var i=0, len = elems.length; i < len; i++ ) {
    1530       var elem = elems[i];
    1531       var attr = elem.getAttribute( dataAttr );
    1532       var options;
    1533       try {
    1534         options = attr && JSON.parse( attr );
    1535       } catch ( error ) {
    1536         // log error, do not initialize
    1537         if ( console ) {
    1538           console.error( 'Error parsing ' + dataAttr + ' on ' +
    1539             elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' +
    1540             error );
    1541         }
    1542         continue;
    1543       }
    1544       // initialize
    1545       var instance = new WidgetClass( elem, options );
    1546       // make available via $().data('layoutname')
    1547       var jQuery = window.jQuery;
    1548       if ( jQuery ) {
    1549         jQuery.data( elem, namespace, instance );
    1550       }
    1551     }
    1552   });
    1553 };
    1554 
    1555 // -----  ----- //
    1556 
    1557 return utils;
    1558 
    1559 }));
    1560 
    1561 /**
    1562  * Outlayer Item
    1563  */
    1564 
    1565 ( function( window, factory ) {
    1566   
    1567   // universal module definition
    1568   if ( typeof define === 'function' && define.amd ) {
    1569     // AMD
    1570     define( 'outlayer/item',[
    1571         'eventEmitter/EventEmitter',
    1572         'get-size/get-size',
    1573         'get-style-property/get-style-property',
    1574         'fizzy-ui-utils/utils'
    1575       ],
    1576       function( EventEmitter, getSize, getStyleProperty, utils ) {
    1577         return factory( window, EventEmitter, getSize, getStyleProperty, utils );
    1578       }
    1579     );
    1580   } else if (typeof exports === 'object') {
    1581     // CommonJS
    1582     module.exports = factory(
    1583       window,
    1584       require('wolfy87-eventemitter'),
    1585       require('get-size'),
    1586       require('desandro-get-style-property'),
    1587       require('fizzy-ui-utils')
    1588     );
    1589   } else {
    1590     // browser global
    1591     window.Outlayer = {};
    1592     window.Outlayer.Item = factory(
    1593       window,
    1594       window.EventEmitter,
    1595       window.getSize,
    1596       window.getStyleProperty,
    1597       window.fizzyUIUtils
    1598     );
    1599   }
    1600 
    1601 }( window, function factory( window, EventEmitter, getSize, getStyleProperty, utils ) {
    1602 
    1603 
    1604 // ----- helpers ----- //
    1605 
    1606 var getComputedStyle = window.getComputedStyle;
    1607 var getStyle = getComputedStyle ?
    1608   function( elem ) {
    1609     return getComputedStyle( elem, null );
    1610   } :
    1611   function( elem ) {
    1612     return elem.currentStyle;
    1613   };
    1614 
    1615 
    1616 function isEmptyObj( obj ) {
    1617   for ( var prop in obj ) {
    1618     return false;
    1619   }
    1620   prop = null;
    1621   return true;
    1622 }
    1623 
    1624 // -------------------------- CSS3 support -------------------------- //
    1625 
    1626 var transitionProperty = getStyleProperty('transition');
    1627 var transformProperty = getStyleProperty('transform');
    1628 var supportsCSS3 = transitionProperty && transformProperty;
    1629 var is3d = !!getStyleProperty('perspective');
    1630 
    1631 var transitionEndEvent = {
    1632   WebkitTransition: 'webkitTransitionEnd',
    1633   MozTransition: 'transitionend',
    1634   OTransition: 'otransitionend',
    1635   transition: 'transitionend'
    1636 }[ transitionProperty ];
    1637 
    1638 // properties that could have vendor prefix
    1639 var prefixableProperties = [
    1640   'transform',
    1641   'transition',
    1642   'transitionDuration',
    1643   'transitionProperty'
    1644 ];
    1645 
    1646 // cache all vendor properties
    1647 var vendorProperties = ( function() {
    1648   var cache = {};
    1649   for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
    1650     var prop = prefixableProperties[i];
    1651     var supportedProp = getStyleProperty( prop );
    1652     if ( supportedProp && supportedProp !== prop ) {
    1653       cache[ prop ] = supportedProp;
    1654     }
    1655   }
    1656   return cache;
    1657 })();
    1658 
    1659 // -------------------------- Item -------------------------- //
    1660 
    1661 function Item( element, layout ) {
    1662   if ( !element ) {
    1663     return;
    1664   }
    1665 
    1666   this.element = element;
    1667   // parent layout class, i.e. Masonry, Isotope, or Packery
    1668   this.layout = layout;
    1669   this.position = {
    1670     x: 0,
    1671     y: 0
    1672   };
    1673 
    1674   this._create();
    1675 }
    1676 
    1677 // inherit EventEmitter
    1678 utils.extend( Item.prototype, EventEmitter.prototype );
    1679 
    1680 Item.prototype._create = function() {
    1681   // transition objects
    1682   this._transn = {
    1683     ingProperties: {},
    1684     clean: {},
    1685     onEnd: {}
    1686   };
    1687 
    1688   this.css({
    1689     position: 'absolute'
    1690   });
    1691 };
    1692 
    1693 // trigger specified handler for event type
    1694 Item.prototype.handleEvent = function( event ) {
    1695   var method = 'on' + event.type;
    1696   if ( this[ method ] ) {
    1697     this[ method ]( event );
    1698   }
    1699 };
    1700 
    1701 Item.prototype.getSize = function() {
    1702   this.size = getSize( this.element );
    1703 };
    1704 
    1705 /**
    1706  * apply CSS styles to element
    1707  * @param {Object} style
    1708  */
    1709 Item.prototype.css = function( style ) {
    1710   var elemStyle = this.element.style;
    1711 
    1712   for ( var prop in style ) {
    1713     // use vendor property if available
    1714     var supportedProp = vendorProperties[ prop ] || prop;
    1715     elemStyle[ supportedProp ] = style[ prop ];
    1716   }
    1717 };
    1718 
    1719  // measure position, and sets it
    1720 Item.prototype.getPosition = function() {
    1721   var style = getStyle( this.element );
    1722   var layoutOptions = this.layout.options;
    1723   var isOriginLeft = layoutOptions.isOriginLeft;
    1724   var isOriginTop = layoutOptions.isOriginTop;
    1725   var xValue = style[ isOriginLeft ? 'left' : 'right' ];
    1726   var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
    1727   // convert percent to pixels
    1728   var layoutSize = this.layout.size;
    1729   var x = xValue.indexOf('%') != -1 ?
    1730     ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 );
    1731   var y = yValue.indexOf('%') != -1 ?
    1732     ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 );
    1733 
    1734   // clean up 'auto' or other non-integer values
    1735   x = isNaN( x ) ? 0 : x;
    1736   y = isNaN( y ) ? 0 : y;
    1737   // remove padding from measurement
    1738   x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
    1739   y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
    1740 
    1741   this.position.x = x;
    1742   this.position.y = y;
    1743 };
    1744 
    1745 // set settled position, apply padding
    1746 Item.prototype.layoutPosition = function() {
    1747   var layoutSize = this.layout.size;
    1748   var layoutOptions = this.layout.options;
    1749   var style = {};
    1750 
    1751   // x
    1752   var xPadding = layoutOptions.isOriginLeft ? 'paddingLeft' : 'paddingRight';
    1753   var xProperty = layoutOptions.isOriginLeft ? 'left' : 'right';
    1754   var xResetProperty = layoutOptions.isOriginLeft ? 'right' : 'left';
    1755 
    1756   var x = this.position.x + layoutSize[ xPadding ];
    1757   // set in percentage or pixels
    1758   style[ xProperty ] = this.getXValue( x );
    1759   // reset other property
    1760   style[ xResetProperty ] = '';
    1761 
    1762   // y
    1763   var yPadding = layoutOptions.isOriginTop ? 'paddingTop' : 'paddingBottom';
    1764   var yProperty = layoutOptions.isOriginTop ? 'top' : 'bottom';
    1765   var yResetProperty = layoutOptions.isOriginTop ? 'bottom' : 'top';
    1766 
    1767   var y = this.position.y + layoutSize[ yPadding ];
    1768   // set in percentage or pixels
    1769   style[ yProperty ] = this.getYValue( y );
    1770   // reset other property
    1771   style[ yResetProperty ] = '';
    1772 
    1773   this.css( style );
    1774   this.emitEvent( 'layout', [ this ] );
    1775 };
    1776 
    1777 Item.prototype.getXValue = function( x ) {
    1778   var layoutOptions = this.layout.options;
    1779   return layoutOptions.percentPosition && !layoutOptions.isHorizontal ?
    1780     ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
    1781 };
    1782 
    1783 Item.prototype.getYValue = function( y ) {
    1784   var layoutOptions = this.layout.options;
    1785   return layoutOptions.percentPosition && layoutOptions.isHorizontal ?
    1786     ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
    1787 };
    1788 
    1789 
    1790 Item.prototype._transitionTo = function( x, y ) {
    1791   this.getPosition();
    1792   // get current x & y from top/left
    1793   var curX = this.position.x;
    1794   var curY = this.position.y;
    1795 
    1796   var compareX = parseInt( x, 10 );
    1797   var compareY = parseInt( y, 10 );
    1798   var didNotMove = compareX === this.position.x && compareY === this.position.y;
    1799 
    1800   // save end position
    1801   this.setPosition( x, y );
    1802 
    1803   // if did not move and not transitioning, just go to layout
    1804   if ( didNotMove && !this.isTransitioning ) {
    1805     this.layoutPosition();
    1806     return;
    1807   }
    1808 
    1809   var transX = x - curX;
    1810   var transY = y - curY;
    1811   var transitionStyle = {};
    1812   transitionStyle.transform = this.getTranslate( transX, transY );
    1813 
    1814   this.transition({
    1815     to: transitionStyle,
    1816     onTransitionEnd: {
    1817       transform: this.layoutPosition
    1818     },
    1819     isCleaning: true
    1820   });
    1821 };
    1822 
    1823 Item.prototype.getTranslate = function( x, y ) {
    1824   // flip cooridinates if origin on right or bottom
    1825   var layoutOptions = this.layout.options;
    1826   x = layoutOptions.isOriginLeft ? x : -x;
    1827   y = layoutOptions.isOriginTop ? y : -y;
    1828 
    1829   if ( is3d ) {
    1830     return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
    1831   }
    1832 
    1833   return 'translate(' + x + 'px, ' + y + 'px)';
    1834 };
    1835 
    1836 // non transition + transform support
    1837 Item.prototype.goTo = function( x, y ) {
    1838   this.setPosition( x, y );
    1839   this.layoutPosition();
    1840 };
    1841 
    1842 // use transition and transforms if supported
    1843 Item.prototype.moveTo = supportsCSS3 ?
    1844   Item.prototype._transitionTo : Item.prototype.goTo;
    1845 
    1846 Item.prototype.setPosition = function( x, y ) {
    1847   this.position.x = parseInt( x, 10 );
    1848   this.position.y = parseInt( y, 10 );
    1849 };
    1850 
    1851 // ----- transition ----- //
    1852 
    1853 /**
    1854  * @param {Object} style - CSS
    1855  * @param {Function} onTransitionEnd
    1856  */
    1857 
    1858 // non transition, just trigger callback
    1859 Item.prototype._nonTransition = function( args ) {
    1860   this.css( args.to );
    1861   if ( args.isCleaning ) {
    1862     this._removeStyles( args.to );
    1863   }
    1864   for ( var prop in args.onTransitionEnd ) {
    1865     args.onTransitionEnd[ prop ].call( this );
    1866   }
    1867 };
    1868 
    1869 /**
    1870  * proper transition
    1871  * @param {Object} args - arguments
    1872  *   @param {Object} to - style to transition to
    1873  *   @param {Object} from - style to start transition from
    1874  *   @param {Boolean} isCleaning - removes transition styles after transition
    1875  *   @param {Function} onTransitionEnd - callback
    1876  */
    1877 Item.prototype._transition = function( args ) {
    1878   // redirect to nonTransition if no transition duration
    1879   if ( !parseFloat( this.layout.options.transitionDuration ) ) {
    1880     this._nonTransition( args );
    1881     return;
    1882   }
    1883 
    1884   var _transition = this._transn;
    1885   // keep track of onTransitionEnd callback by css property
    1886   for ( var prop in args.onTransitionEnd ) {
    1887     _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
    1888   }
    1889   // keep track of properties that are transitioning
    1890   for ( prop in args.to ) {
    1891     _transition.ingProperties[ prop ] = true;
    1892     // keep track of properties to clean up when transition is done
    1893     if ( args.isCleaning ) {
    1894       _transition.clean[ prop ] = true;
    1895     }
    1896   }
    1897 
    1898   // set from styles
    1899   if ( args.from ) {
    1900     this.css( args.from );
    1901     // force redraw. http://blog.alexmaccaw.com/css-transitions
    1902     var h = this.element.offsetHeight;
    1903     // hack for JSHint to hush about unused var
    1904     h = null;
    1905   }
    1906   // enable transition
    1907   this.enableTransition( args.to );
    1908   // set styles that are transitioning
    1909   this.css( args.to );
    1910 
    1911   this.isTransitioning = true;
    1912 
    1913 };
    1914 
    1915 // dash before all cap letters, including first for
    1916 // WebkitTransform => -webkit-transform
    1917 function toDashedAll( str ) {
    1918   return str.replace( /([A-Z])/g, function( $1 ) {
    1919     return '-' + $1.toLowerCase();
    1920   });
    1921 }
    1922 
    1923 var transitionProps = 'opacity,' +
    1924   toDashedAll( vendorProperties.transform || 'transform' );
    1925 
    1926 Item.prototype.enableTransition = function(/* style */) {
    1927   // HACK changing transitionProperty during a transition
    1928   // will cause transition to jump
    1929   if ( this.isTransitioning ) {
    1930     return;
    1931   }
    1932 
    1933   // make `transition: foo, bar, baz` from style object
    1934   // HACK un-comment this when enableTransition can work
    1935   // while a transition is happening
    1936   // var transitionValues = [];
    1937   // for ( var prop in style ) {
    1938   //   // dash-ify camelCased properties like WebkitTransition
    1939   //   prop = vendorProperties[ prop ] || prop;
    1940   //   transitionValues.push( toDashedAll( prop ) );
    1941   // }
    1942   // enable transition styles
    1943   this.css({
    1944     transitionProperty: transitionProps,
    1945     transitionDuration: this.layout.options.transitionDuration
    1946   });
    1947   // listen for transition end event
    1948   this.element.addEventListener( transitionEndEvent, this, false );
    1949 };
    1950 
    1951 Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
    1952 
    1953 // ----- events ----- //
    1954 
    1955 Item.prototype.onwebkitTransitionEnd = function( event ) {
    1956   this.ontransitionend( event );
    1957 };
    1958 
    1959 Item.prototype.onotransitionend = function( event ) {
    1960   this.ontransitionend( event );
    1961 };
    1962 
    1963 // properties that I munge to make my life easier
    1964 var dashedVendorProperties = {
    1965   '-webkit-transform': 'transform',
    1966   '-moz-transform': 'transform',
    1967   '-o-transform': 'transform'
    1968 };
    1969 
    1970 Item.prototype.ontransitionend = function( event ) {
    1971   // disregard bubbled events from children
    1972   if ( event.target !== this.element ) {
    1973     return;
    1974   }
    1975   var _transition = this._transn;
    1976   // get property name of transitioned property, convert to prefix-free
    1977   var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
    1978 
    1979   // remove property that has completed transitioning
    1980   delete _transition.ingProperties[ propertyName ];
    1981   // check if any properties are still transitioning
    1982   if ( isEmptyObj( _transition.ingProperties ) ) {
    1983     // all properties have completed transitioning
    1984     this.disableTransition();
    1985   }
    1986   // clean style
    1987   if ( propertyName in _transition.clean ) {
    1988     // clean up style
    1989     this.element.style[ event.propertyName ] = '';
    1990     delete _transition.clean[ propertyName ];
    1991   }
    1992   // trigger onTransitionEnd callback
    1993   if ( propertyName in _transition.onEnd ) {
    1994     var onTransitionEnd = _transition.onEnd[ propertyName ];
    1995     onTransitionEnd.call( this );
    1996     delete _transition.onEnd[ propertyName ];
    1997   }
    1998 
    1999   this.emitEvent( 'transitionEnd', [ this ] );
    2000 };
    2001 
    2002 Item.prototype.disableTransition = function() {
    2003   this.removeTransitionStyles();
    2004   this.element.removeEventListener( transitionEndEvent, this, false );
    2005   this.isTransitioning = false;
    2006 };
    2007 
    2008 /**
    2009  * removes style property from element
    2010  * @param {Object} style
    2011 **/
    2012 Item.prototype._removeStyles = function( style ) {
    2013   // clean up transition styles
    2014   var cleanStyle = {};
    2015   for ( var prop in style ) {
    2016     cleanStyle[ prop ] = '';
    2017   }
    2018   this.css( cleanStyle );
    2019 };
    2020 
    2021 var cleanTransitionStyle = {
    2022   transitionProperty: '',
    2023   transitionDuration: ''
    2024 };
    2025 
    2026 Item.prototype.removeTransitionStyles = function() {
    2027   // remove transition
    2028   this.css( cleanTransitionStyle );
    2029 };
    2030 
    2031 // ----- show/hide/remove ----- //
    2032 
    2033 // remove element from DOM
    2034 Item.prototype.removeElem = function() {
    2035   this.element.parentNode.removeChild( this.element );
    2036   // remove display: none
    2037   this.css({ display: '' });
    2038   this.emitEvent( 'remove', [ this ] );
    2039 };
    2040 
    2041 Item.prototype.remove = function() {
    2042   // just remove element if no transition support or no transition
    2043   if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
    2044     this.removeElem();
    2045     return;
    2046   }
    2047 
    2048   // start transition
    2049   var _this = this;
    2050   this.once( 'transitionEnd', function() {
    2051     _this.removeElem();
    2052   });
    2053   this.hide();
    2054 };
    2055 
    2056 Item.prototype.reveal = function() {
    2057   delete this.isHidden;
    2058   // remove display: none
    2059   this.css({ display: '' });
    2060 
    2061   var options = this.layout.options;
    2062 
    2063   var onTransitionEnd = {};
    2064   var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
    2065   onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
    2066 
    2067   this.transition({
    2068     from: options.hiddenStyle,
    2069     to: options.visibleStyle,
    2070     isCleaning: true,
    2071     onTransitionEnd: onTransitionEnd
    2072   });
    2073 };
    2074 
    2075 Item.prototype.onRevealTransitionEnd = function() {
    2076   // check if still visible
    2077   // during transition, item may have been hidden
    2078   if ( !this.isHidden ) {
    2079     this.emitEvent('reveal');
    2080   }
    2081 };
    2082 
    2083 /**
    2084  * get style property use for hide/reveal transition end
    2085  * @param {String} styleProperty - hiddenStyle/visibleStyle
    2086  * @returns {String}
    2087  */
    2088 Item.prototype.getHideRevealTransitionEndProperty = function( styleProperty ) {
    2089   var optionStyle = this.layout.options[ styleProperty ];
    2090   // use opacity
    2091   if ( optionStyle.opacity ) {
    2092     return 'opacity';
    2093   }
    2094   // get first property
    2095   for ( var prop in optionStyle ) {
    2096     return prop;
    2097   }
    2098 };
    2099 
    2100 Item.prototype.hide = function() {
    2101   // set flag
    2102   this.isHidden = true;
    2103   // remove display: none
    2104   this.css({ display: '' });
    2105 
    2106   var options = this.layout.options;
    2107 
    2108   var onTransitionEnd = {};
    2109   var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
    2110   onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
    2111 
    2112   this.transition({
    2113     from: options.visibleStyle,
    2114     to: options.hiddenStyle,
    2115     // keep hidden stuff hidden
    2116     isCleaning: true,
    2117     onTransitionEnd: onTransitionEnd
    2118   });
    2119 };
    2120 
    2121 Item.prototype.onHideTransitionEnd = function() {
    2122   // check if still hidden
    2123   // during transition, item may have been un-hidden
    2124   if ( this.isHidden ) {
    2125     this.css({ display: 'none' });
    2126     this.emitEvent('hide');
    2127   }
    2128 };
    2129 
    2130 Item.prototype.destroy = function() {
    2131   this.css({
    2132     position: '',
    2133     left: '',
    2134     right: '',
    2135     top: '',
    2136     bottom: '',
    2137     transition: '',
    2138     transform: ''
    2139   });
    2140 };
    2141 
    2142 return Item;
    2143 
    2144 }));
    2145 
    2146 /*!
    2147  * Outlayer v1.4.2
    2148  * the brains and guts of a layout library
    2149  * MIT license
    2150  */
    2151 
    2152 ( function( window, factory ) {
    2153   
    2154   // universal module definition
    2155 
    2156   if ( typeof define == 'function' && define.amd ) {
    2157     // AMD
    2158     define( 'outlayer/outlayer',[
    2159         'eventie/eventie',
    2160         'eventEmitter/EventEmitter',
    2161         'get-size/get-size',
    2162         'fizzy-ui-utils/utils',
    2163         './item'
    2164       ],
    2165       function( eventie, EventEmitter, getSize, utils, Item ) {
    2166         return factory( window, eventie, EventEmitter, getSize, utils, Item);
    2167       }
    2168     );
    2169   } else if ( typeof exports == 'object' ) {
    2170     // CommonJS
    2171     module.exports = factory(
    2172       window,
    2173       require('eventie'),
    2174       require('wolfy87-eventemitter'),
    2175       require('get-size'),
    2176       require('fizzy-ui-utils'),
    2177       require('./item')
    2178     );
    2179   } else {
    2180     // browser global
    2181     window.Outlayer = factory(
    2182       window,
    2183       window.eventie,
    2184       window.EventEmitter,
    2185       window.getSize,
    2186       window.fizzyUIUtils,
    2187       window.Outlayer.Item
    2188     );
    2189   }
    2190 
    2191 }( window, function factory( window, eventie, EventEmitter, getSize, utils, Item ) {
    2192 
    2193 
    2194 // ----- vars ----- //
    2195 
    2196 var console = window.console;
    2197 var jQuery = window.jQuery;
    2198 var noop = function() {};
    2199 
    2200 // -------------------------- Outlayer -------------------------- //
    2201 
    2202 // globally unique identifiers
    2203 var GUID = 0;
    2204 // internal store of all Outlayer intances
    2205 var instances = {};
    2206 
    2207 
    2208 /**
    2209  * @param {Element, String} element
    2210  * @param {Object} options
    2211  * @constructor
    2212  */
    2213 function Outlayer( element, options ) {
    2214   var queryElement = utils.getQueryElement( element );
    2215   if ( !queryElement ) {
    2216     if ( console ) {
    2217       console.error( 'Bad element for ' + this.constructor.namespace +
    2218         ': ' + ( queryElement || element ) );
    2219     }
    2220     return;
    2221   }
    2222   this.element = queryElement;
    2223   // add jQuery
    2224   if ( jQuery ) {
    2225     this.$element = jQuery( this.element );
    2226   }
    2227 
    2228   // options
    2229   this.options = utils.extend( {}, this.constructor.defaults );
    2230   this.option( options );
    2231 
    2232   // add id for Outlayer.getFromElement
    2233   var id = ++GUID;
    2234   this.element.outlayerGUID = id; // expando
    2235   instances[ id ] = this; // associate via id
    2236 
    2237   // kick it off
    2238   this._create();
    2239 
    2240   if ( this.options.isInitLayout ) {
    2241     this.layout();
    2242   }
    2243 }
    2244 
    2245 // settings are for internal use only
    2246 Outlayer.namespace = 'outlayer';
    2247 Outlayer.Item = Item;
    2248 
    2249 // default options
    2250 Outlayer.defaults = {
    2251   containerStyle: {
    2252     position: 'relative'
    2253   },
    2254   isInitLayout: true,
    2255   isOriginLeft: true,
    2256   isOriginTop: true,
    2257   isResizeBound: true,
    2258   isResizingContainer: true,
    2259   // item options
    2260   transitionDuration: '0.4s',
    2261   hiddenStyle: {
    2262     opacity: 0,
    2263     transform: 'scale(0.001)'
    2264   },
    2265   visibleStyle: {
    2266     opacity: 1,
    2267     transform: 'scale(1)'
    2268   }
    2269 };
    2270 
    2271 // inherit EventEmitter
    2272 utils.extend( Outlayer.prototype, EventEmitter.prototype );
    2273 
    2274 /**
    2275  * set options
    2276  * @param {Object} opts
    2277  */
    2278 Outlayer.prototype.option = function( opts ) {
    2279   utils.extend( this.options, opts );
    2280 };
    2281 
    2282 Outlayer.prototype._create = function() {
    2283   // get items from children
    2284   this.reloadItems();
    2285   // elements that affect layout, but are not laid out
    2286   this.stamps = [];
    2287   this.stamp( this.options.stamp );
    2288   // set container style
    2289   utils.extend( this.element.style, this.options.containerStyle );
    2290 
    2291   // bind resize method
    2292   if ( this.options.isResizeBound ) {
    2293     this.bindResize();
    2294   }
    2295 };
    2296 
    2297 // goes through all children again and gets bricks in proper order
    2298 Outlayer.prototype.reloadItems = function() {
    2299   // collection of item elements
    2300   this.items = this._itemize( this.element.children );
    2301 };
    2302 
    2303 
    2304 /**
    2305  * turn elements into Outlayer.Items to be used in layout
    2306  * @param {Array or NodeList or HTMLElement} elems
    2307  * @returns {Array} items - collection of new Outlayer Items
    2308  */
    2309 Outlayer.prototype._itemize = function( elems ) {
    2310 
    2311   var itemElems = this._filterFindItemElements( elems );
    2312   var Item = this.constructor.Item;
    2313 
    2314   // create new Outlayer Items for collection
    2315   var items = [];
    2316   for ( var i=0, len = itemElems.length; i < len; i++ ) {
    2317     var elem = itemElems[i];
    2318     var item = new Item( elem, this );
    2319     items.push( item );
    2320   }
    2321 
    2322   return items;
    2323 };
    2324 
    2325 /**
    2326  * get item elements to be used in layout
    2327  * @param {Array or NodeList or HTMLElement} elems
    2328  * @returns {Array} items - item elements
    2329  */
    2330 Outlayer.prototype._filterFindItemElements = function( elems ) {
    2331   return utils.filterFindElements( elems, this.options.itemSelector );
    2332 };
    2333 
    2334 /**
    2335  * getter method for getting item elements
    2336  * @returns {Array} elems - collection of item elements
    2337  */
    2338 Outlayer.prototype.getItemElements = function() {
    2339   var elems = [];
    2340   for ( var i=0, len = this.items.length; i < len; i++ ) {
    2341     elems.push( this.items[i].element );
    2342   }
    2343   return elems;
    2344 };
    2345 
    2346 // ----- init & layout ----- //
    2347 
    2348 /**
    2349  * lays out all items
    2350  */
    2351 Outlayer.prototype.layout = function() {
    2352   this._resetLayout();
    2353   this._manageStamps();
    2354 
    2355   // don't animate first layout
    2356   var isInstant = this.options.isLayoutInstant !== undefined ?
    2357     this.options.isLayoutInstant : !this._isLayoutInited;
    2358   this.layoutItems( this.items, isInstant );
    2359 
    2360   // flag for initalized
    2361   this._isLayoutInited = true;
    2362 };
    2363 
    2364 // _init is alias for layout
    2365 Outlayer.prototype._init = Outlayer.prototype.layout;
    2366 
    2367 /**
    2368  * logic before any new layout
    2369  */
    2370 Outlayer.prototype._resetLayout = function() {
    2371   this.getSize();
    2372 };
    2373 
    2374 
    2375 Outlayer.prototype.getSize = function() {
    2376   this.size = getSize( this.element );
    2377 };
    2378 
    2379 /**
    2380  * get measurement from option, for columnWidth, rowHeight, gutter
    2381  * if option is String -> get element from selector string, & get size of element
    2382  * if option is Element -> get size of element
    2383  * else use option as a number
    2384  *
    2385  * @param {String} measurement
    2386  * @param {String} size - width or height
    2387  * @private
    2388  */
    2389 Outlayer.prototype._getMeasurement = function( measurement, size ) {
    2390   var option = this.options[ measurement ];
    2391   var elem;
    2392   if ( !option ) {
    2393     // default to 0
    2394     this[ measurement ] = 0;
    2395   } else {
    2396     // use option as an element
    2397     if ( typeof option === 'string' ) {
    2398       elem = this.element.querySelector( option );
    2399     } else if ( utils.isElement( option ) ) {
    2400       elem = option;
    2401     }
    2402     // use size of element, if element
    2403     this[ measurement ] = elem ? getSize( elem )[ size ] : option;
    2404   }
    2405 };
    2406 
    2407 /**
    2408  * layout a collection of item elements
    2409  * @api public
    2410  */
    2411 Outlayer.prototype.layoutItems = function( items, isInstant ) {
    2412   items = this._getItemsForLayout( items );
    2413 
    2414   this._layoutItems( items, isInstant );
    2415 
    2416   this._postLayout();
    2417 };
    2418 
    2419 /**
    2420  * get the items to be laid out
    2421  * you may want to skip over some items
    2422  * @param {Array} items
    2423  * @returns {Array} items
    2424  */
    2425 Outlayer.prototype._getItemsForLayout = function( items ) {
    2426   var layoutItems = [];
    2427   for ( var i=0, len = items.length; i < len; i++ ) {
    2428     var item = items[i];
    2429     if ( !item.isIgnored ) {
    2430       layoutItems.push( item );
    2431     }
    2432   }
    2433   return layoutItems;
    2434 };
    2435 
    2436 /**
    2437  * layout items
    2438  * @param {Array} items
    2439  * @param {Boolean} isInstant
    2440  */
    2441 Outlayer.prototype._layoutItems = function( items, isInstant ) {
    2442   this._emitCompleteOnItems( 'layout', items );
    2443 
    2444   if ( !items || !items.length ) {
    2445     // no items, emit event with empty array
    2446     return;
    2447   }
    2448 
    2449   var queue = [];
    2450 
    2451   for ( var i=0, len = items.length; i < len; i++ ) {
    2452     var item = items[i];
    2453     // get x/y object from method
    2454     var position = this._getItemLayoutPosition( item );
    2455     // enqueue
    2456     position.item = item;
    2457     position.isInstant = isInstant || item.isLayoutInstant;
    2458     queue.push( position );
    2459   }
    2460 
    2461   this._processLayoutQueue( queue );
    2462 };
    2463 
    2464 /**
    2465  * get item layout position
    2466  * @param {Outlayer.Item} item
    2467  * @returns {Object} x and y position
    2468  */
    2469 Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) {
    2470   return {
    2471     x: 0,
    2472     y: 0
    2473   };
    2474 };
    2475 
    2476 /**
    2477  * iterate over array and position each item
    2478  * Reason being - separating this logic prevents 'layout invalidation'
    2479  * thx @paul_irish
    2480  * @param {Array} queue
    2481  */
    2482 Outlayer.prototype._processLayoutQueue = function( queue ) {
    2483   for ( var i=0, len = queue.length; i < len; i++ ) {
    2484     var obj = queue[i];
    2485     this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
    2486   }
    2487 };
    2488 
    2489 /**
    2490  * Sets position of item in DOM
    2491  * @param {Outlayer.Item} item
    2492  * @param {Number} x - horizontal position
    2493  * @param {Number} y - vertical position
    2494  * @param {Boolean} isInstant - disables transitions
    2495  */
    2496 Outlayer.prototype._positionItem = function( item, x, y, isInstant ) {
    2497   if ( isInstant ) {
    2498     // if not transition, just set CSS
    2499     item.goTo( x, y );
    2500   } else {
    2501     item.moveTo( x, y );
    2502   }
    2503 };
    2504 
    2505 /**
    2506  * Any logic you want to do after each layout,
    2507  * i.e. size the container
    2508  */
    2509 Outlayer.prototype._postLayout = function() {
    2510   this.resizeContainer();
    2511 };
    2512 
    2513 Outlayer.prototype.resizeContainer = function() {
    2514   if ( !this.options.isResizingContainer ) {
    2515     return;
    2516   }
    2517   var size = this._getContainerSize();
    2518   if ( size ) {
    2519     this._setContainerMeasure( size.width, true );
    2520     this._setContainerMeasure( size.height, false );
    2521   }
    2522 };
    2523 
    2524 /**
    2525  * Sets width or height of container if returned
    2526  * @returns {Object} size
    2527  *   @param {Number} width
    2528  *   @param {Number} height
    2529  */
    2530 Outlayer.prototype._getContainerSize = noop;
    2531 
    2532 /**
    2533  * @param {Number} measure - size of width or height
    2534  * @param {Boolean} isWidth
    2535  */
    2536 Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) {
    2537   if ( measure === undefined ) {
    2538     return;
    2539   }
    2540 
    2541   var elemSize = this.size;
    2542   // add padding and border width if border box
    2543   if ( elemSize.isBorderBox ) {
    2544     measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
    2545       elemSize.borderLeftWidth + elemSize.borderRightWidth :
    2546       elemSize.paddingBottom + elemSize.paddingTop +
    2547       elemSize.borderTopWidth + elemSize.borderBottomWidth;
    2548   }
    2549 
    2550   measure = Math.max( measure, 0 );
    2551   this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
    2552 };
    2553 
    2554 /**
    2555  * emit eventComplete on a collection of items events
    2556  * @param {String} eventName
    2557  * @param {Array} items - Outlayer.Items
    2558  */
    2559 Outlayer.prototype._emitCompleteOnItems = function( eventName, items ) {
    2560   var _this = this;
    2561   function onComplete() {
    2562     _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
    2563   }
    2564 
    2565   var count = items.length;
    2566   if ( !items || !count ) {
    2567     onComplete();
    2568     return;
    2569   }
    2570 
    2571   var doneCount = 0;
    2572   function tick() {
    2573     doneCount++;
    2574     if ( doneCount === count ) {
    2575       onComplete();
    2576     }
    2577   }
    2578 
    2579   // bind callback
    2580   for ( var i=0, len = items.length; i < len; i++ ) {
    2581     var item = items[i];
    2582     item.once( eventName, tick );
    2583   }
    2584 };
    2585 
    2586 /**
    2587  * emits events via eventEmitter and jQuery events
    2588  * @param {String} type - name of event
    2589  * @param {Event} event - original event
    2590  * @param {Array} args - extra arguments
    2591  */
    2592 Outlayer.prototype.dispatchEvent = function( type, event, args ) {
    2593   // add original event to arguments
    2594   var emitArgs = event ? [ event ].concat( args ) : args;
    2595   this.emitEvent( type, emitArgs );
    2596 
    2597   if ( jQuery ) {
    2598     // set this.$element
    2599     this.$element = this.$element || jQuery( this.element );
    2600     if ( event ) {
    2601       // create jQuery event
    2602       var $event = jQuery.Event( event );
    2603       $event.type = type;
    2604       this.$element.trigger( $event, args );
    2605     } else {
    2606       // just trigger with type if no event available
    2607       this.$element.trigger( type, args );
    2608     }
    2609   }
    2610 };
    2611 
    2612 // -------------------------- ignore & stamps -------------------------- //
    2613 
    2614 
    2615 /**
    2616  * keep item in collection, but do not lay it out
    2617  * ignored items do not get skipped in layout
    2618  * @param {Element} elem
    2619  */
    2620 Outlayer.prototype.ignore = function( elem ) {
    2621   var item = this.getItem( elem );
    2622   if ( item ) {
    2623     item.isIgnored = true;
    2624   }
    2625 };
    2626 
    2627 /**
    2628  * return item to layout collection
    2629  * @param {Element} elem
    2630  */
    2631 Outlayer.prototype.unignore = function( elem ) {
    2632   var item = this.getItem( elem );
    2633   if ( item ) {
    2634     delete item.isIgnored;
    2635   }
    2636 };
    2637 
    2638 /**
    2639  * adds elements to stamps
    2640  * @param {NodeList, Array, Element, or String} elems
    2641  */
    2642 Outlayer.prototype.stamp = function( elems ) {
    2643   elems = this._find( elems );
    2644   if ( !elems ) {
    2645     return;
    2646   }
    2647 
    2648   this.stamps = this.stamps.concat( elems );
    2649   // ignore
    2650   for ( var i=0, len = elems.length; i < len; i++ ) {
    2651     var elem = elems[i];
    2652     this.ignore( elem );
    2653   }
    2654 };
    2655 
    2656 /**
    2657  * removes elements to stamps
    2658  * @param {NodeList, Array, or Element} elems
    2659  */
    2660 Outlayer.prototype.unstamp = function( elems ) {
    2661   elems = this._find( elems );
    2662   if ( !elems ){
    2663     return;
    2664   }
    2665 
    2666   for ( var i=0, len = elems.length; i < len; i++ ) {
    2667     var elem = elems[i];
    2668     // filter out removed stamp elements
    2669     utils.removeFrom( this.stamps, elem );
    2670     this.unignore( elem );
    2671   }
    2672 
    2673 };
    2674 
    2675 /**
    2676  * finds child elements
    2677  * @param {NodeList, Array, Element, or String} elems
    2678  * @returns {Array} elems
    2679  */
    2680 Outlayer.prototype._find = function( elems ) {
    2681   if ( !elems ) {
    2682     return;
    2683   }
    2684   // if string, use argument as selector string
    2685   if ( typeof elems === 'string' ) {
    2686     elems = this.element.querySelectorAll( elems );
    2687   }
    2688   elems = utils.makeArray( elems );
    2689   return elems;
    2690 };
    2691 
    2692 Outlayer.prototype._manageStamps = function() {
    2693   if ( !this.stamps || !this.stamps.length ) {
    2694     return;
    2695   }
    2696 
    2697   this._getBoundingRect();
    2698 
    2699   for ( var i=0, len = this.stamps.length; i < len; i++ ) {
    2700     var stamp = this.stamps[i];
    2701     this._manageStamp( stamp );
    2702   }
    2703 };
    2704 
    2705 // update boundingLeft / Top
    2706 Outlayer.prototype._getBoundingRect = function() {
    2707   // get bounding rect for container element
    2708   var boundingRect = this.element.getBoundingClientRect();
    2709   var size = this.size;
    2710   this._boundingRect = {
    2711     left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
    2712     top: boundingRect.top + size.paddingTop + size.borderTopWidth,
    2713     right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
    2714     bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
    2715   };
    2716 };
    2717 
    2718 /**
    2719  * @param {Element} stamp
    2720 **/
    2721 Outlayer.prototype._manageStamp = noop;
    2722 
    2723 /**
    2724  * get x/y position of element relative to container element
    2725  * @param {Element} elem
    2726  * @returns {Object} offset - has left, top, right, bottom
    2727  */
    2728 Outlayer.prototype._getElementOffset = function( elem ) {
    2729   var boundingRect = elem.getBoundingClientRect();
    2730   var thisRect = this._boundingRect;
    2731   var size = getSize( elem );
    2732   var offset = {
    2733     left: boundingRect.left - thisRect.left - size.marginLeft,
    2734     top: boundingRect.top - thisRect.top - size.marginTop,
    2735     right: thisRect.right - boundingRect.right - size.marginRight,
    2736     bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
    2737   };
    2738   return offset;
    2739 };
    2740 
    2741 // -------------------------- resize -------------------------- //
    2742 
    2743 // enable event handlers for listeners
    2744 // i.e. resize -> onresize
    2745 Outlayer.prototype.handleEvent = function( event ) {
    2746   var method = 'on' + event.type;
    2747   if ( this[ method ] ) {
    2748     this[ method ]( event );
    2749   }
    2750 };
    2751 
    2752 /**
    2753  * Bind layout to window resizing
    2754  */
    2755 Outlayer.prototype.bindResize = function() {
    2756   // bind just one listener
    2757   if ( this.isResizeBound ) {
    2758     return;
    2759   }
    2760   window.eventie.bind( window, 'resize', this );
    2761   this.isResizeBound = true;
    2762 };
    2763 
    2764 /**
    2765  * Unbind layout to window resizing
    2766  */
    2767 Outlayer.prototype.unbindResize = function() {
    2768   if ( this.isResizeBound ) {
    2769     window.eventie.unbind( window, 'resize', this );
    2770   }
    2771   this.isResizeBound = false;
    2772 };
    2773 
    2774 // original debounce by John Hann
    2775 // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
    2776 
    2777 // this fires every resize
    2778 Outlayer.prototype.onresize = function() {
    2779   if ( this.resizeTimeout ) {
    2780     clearTimeout( this.resizeTimeout );
    2781   }
    2782 
    2783   var _this = this;
    2784   function delayed() {
    2785     _this.resize();
    2786     delete _this.resizeTimeout;
    2787   }
    2788 
    2789   this.resizeTimeout = setTimeout( delayed, 100 );
    2790 };
    2791 
    2792 // debounced, layout on resize
    2793 Outlayer.prototype.resize = function() {
    2794   // don't trigger if size did not change
    2795   // or if resize was unbound. See #9
    2796   if ( !this.isResizeBound || !this.needsResizeLayout() ) {
    2797     return;
    2798   }
    2799 
    2800   this.layout();
    2801 };
    2802 
    2803 /**
    2804  * check if layout is needed post layout
    2805  * @returns Boolean
    2806  */
    2807 Outlayer.prototype.needsResizeLayout = function() {
    2808   var size = getSize( this.element );
    2809   // check that this.size and size are there
    2810   // IE8 triggers resize on body size change, so they might not be
    2811   var hasSizes = this.size && size;
    2812   return hasSizes && size.innerWidth !== this.size.innerWidth;
    2813 };
    2814 
    2815 // -------------------------- methods -------------------------- //
    2816 
    2817 /**
    2818  * add items to Outlayer instance
    2819  * @param {Array or NodeList or Element} elems
    2820  * @returns {Array} items - Outlayer.Items
    2821 **/
    2822 Outlayer.prototype.addItems = function( elems ) {
    2823   var items = this._itemize( elems );
    2824   // add items to collection
    2825   if ( items.length ) {
    2826     this.items = this.items.concat( items );
    2827   }
    2828   return items;
    2829 };
    2830 
    2831 /**
    2832  * Layout newly-appended item elements
    2833  * @param {Array or NodeList or Element} elems
    2834  */
    2835 Outlayer.prototype.appended = function( elems ) {
    2836   var items = this.addItems( elems );
    2837   if ( !items.length ) {
    2838     return;
    2839   }
    2840   // layout and reveal just the new items
    2841   this.layoutItems( items, true );
    2842   this.reveal( items );
    2843 };
    2844 
    2845 /**
    2846  * Layout prepended elements
    2847  * @param {Array or NodeList or Element} elems
    2848  */
    2849 Outlayer.prototype.prepended = function( elems ) {
    2850   var items = this._itemize( elems );
    2851   if ( !items.length ) {
    2852     return;
    2853   }
    2854   // add items to beginning of collection
    2855   var previousItems = this.items.slice(0);
    2856   this.items = items.concat( previousItems );
    2857   // start new layout
    2858   this._resetLayout();
    2859   this._manageStamps();
    2860   // layout new stuff without transition
    2861   this.layoutItems( items, true );
    2862   this.reveal( items );
    2863   // layout previous items
    2864   this.layoutItems( previousItems );
    2865 };
    2866 
    2867 /**
    2868  * reveal a collection of items
    2869  * @param {Array of Outlayer.Items} items
    2870  */
    2871 Outlayer.prototype.reveal = function( items ) {
    2872   this._emitCompleteOnItems( 'reveal', items );
    2873 
    2874   var len = items && items.length;
    2875   for ( var i=0; len && i < len; i++ ) {
    2876     var item = items[i];
    2877     item.reveal();
    2878   }
    2879 };
    2880 
    2881 /**
    2882  * hide a collection of items
    2883  * @param {Array of Outlayer.Items} items
    2884  */
    2885 Outlayer.prototype.hide = function( items ) {
    2886   this._emitCompleteOnItems( 'hide', items );
    2887 
    2888   var len = items && items.length;
    2889   for ( var i=0; len && i < len; i++ ) {
    2890     var item = items[i];
    2891     item.hide();
    2892   }
    2893 };
    2894 
    2895 /**
    2896  * reveal item elements
    2897  * @param {Array}, {Element}, {NodeList} items
    2898  */
    2899 Outlayer.prototype.revealItemElements = function( elems ) {
    2900   var items = this.getItems( elems );
    2901   this.reveal( items );
    2902 };
    2903 
    2904 /**
    2905  * hide item elements
    2906  * @param {Array}, {Element}, {NodeList} items
    2907  */
    2908 Outlayer.prototype.hideItemElements = function( elems ) {
    2909   var items = this.getItems( elems );
    2910   this.hide( items );
    2911 };
    2912 
    2913 /**
    2914  * get Outlayer.Item, given an Element
    2915  * @param {Element} elem
    2916  * @param {Function} callback
    2917  * @returns {Outlayer.Item} item
    2918  */
    2919 Outlayer.prototype.getItem = function( elem ) {
    2920   // loop through items to get the one that matches
    2921   for ( var i=0, len = this.items.length; i < len; i++ ) {
    2922     var item = this.items[i];
    2923     if ( item.element === elem ) {
    2924       // return item
    2925       return item;
    2926     }
    2927   }
    2928 };
    2929 
    2930 /**
    2931  * get collection of Outlayer.Items, given Elements
    2932  * @param {Array} elems
    2933  * @returns {Array} items - Outlayer.Items
    2934  */
    2935 Outlayer.prototype.getItems = function( elems ) {
    2936   elems = utils.makeArray( elems );
    2937   var items = [];
    2938   for ( var i=0, len = elems.length; i < len; i++ ) {
    2939     var elem = elems[i];
    2940     var item = this.getItem( elem );
    2941     if ( item ) {
    2942       items.push( item );
    2943     }
    2944   }
    2945 
    2946   return items;
    2947 };
    2948 
    2949 /**
    2950  * remove element(s) from instance and DOM
    2951  * @param {Array or NodeList or Element} elems
    2952  */
    2953 Outlayer.prototype.remove = function( elems ) {
    2954   var removeItems = this.getItems( elems );
    2955 
    2956   this._emitCompleteOnItems( 'remove', removeItems );
    2957 
    2958   // bail if no items to remove
    2959   if ( !removeItems || !removeItems.length ) {
    2960     return;
    2961   }
    2962 
    2963   for ( var i=0, len = removeItems.length; i < len; i++ ) {
    2964     var item = removeItems[i];
    2965     item.remove();
    2966     // remove item from collection
    2967     utils.removeFrom( this.items, item );
    2968   }
    2969 };
    2970 
    2971 // ----- destroy ----- //
    2972 
    2973 // remove and disable Outlayer instance
    2974 Outlayer.prototype.destroy = function() {
    2975   // clean up dynamic styles
    2976   var style = this.element.style;
    2977   style.height = '';
    2978   style.position = '';
    2979   style.width = '';
    2980   // destroy items
    2981   for ( var i=0, len = this.items.length; i < len; i++ ) {
    2982     var item = this.items[i];
    2983     item.destroy();
    2984   }
    2985 
    2986   this.unbindResize();
    2987 
    2988   var id = this.element.outlayerGUID;
    2989   delete instances[ id ]; // remove reference to instance by id
    2990   delete this.element.outlayerGUID;
    2991   // remove data for jQuery
    2992   if ( jQuery ) {
    2993     jQuery.removeData( this.element, this.constructor.namespace );
    2994   }
    2995 
    2996 };
    2997 
    2998 // -------------------------- data -------------------------- //
    2999 
    3000 /**
    3001  * get Outlayer instance from element
    3002  * @param {Element} elem
    3003  * @returns {Outlayer}
    3004  */
    3005 Outlayer.data = function( elem ) {
    3006   elem = utils.getQueryElement( elem );
    3007   var id = elem && elem.outlayerGUID;
    3008   return id && instances[ id ];
    3009 };
    3010 
    3011 
    3012 // -------------------------- create Outlayer class -------------------------- //
    3013 
    3014 /**
    3015  * create a layout class
    3016  * @param {String} namespace
    3017  */
    3018 Outlayer.create = function( namespace, options ) {
    3019   // sub-class Outlayer
    3020   function Layout() {
    3021     Outlayer.apply( this, arguments );
    3022   }
    3023   // inherit Outlayer prototype, use Object.create if there
    3024   if ( Object.create ) {
    3025     Layout.prototype = Object.create( Outlayer.prototype );
    3026   } else {
    3027     utils.extend( Layout.prototype, Outlayer.prototype );
    3028   }
    3029   // set contructor, used for namespace and Item
    3030   Layout.prototype.constructor = Layout;
    3031 
    3032   Layout.defaults = utils.extend( {}, Outlayer.defaults );
    3033   // apply new options
    3034   utils.extend( Layout.defaults, options );
    3035   // keep prototype.settings for backwards compatibility (Packery v1.2.0)
    3036   Layout.prototype.settings = {};
    3037 
    3038   Layout.namespace = namespace;
    3039 
    3040   Layout.data = Outlayer.data;
    3041 
    3042   // sub-class Item
    3043   Layout.Item = function LayoutItem() {
    3044     Item.apply( this, arguments );
    3045   };
    3046 
    3047   Layout.Item.prototype = new Item();
    3048 
    3049   // -------------------------- declarative -------------------------- //
    3050 
    3051   utils.htmlInit( Layout, namespace );
    3052 
    3053   // -------------------------- jQuery bridge -------------------------- //
    3054 
    3055   // make into jQuery plugin
    3056   if ( jQuery && jQuery.bridget ) {
    3057     jQuery.bridget( namespace, Layout );
    3058   }
    3059 
    3060   return Layout;
    3061 };
    3062 
    3063 // ----- fin ----- //
    3064 
    3065 // back in global
    3066 Outlayer.Item = Item;
    3067 
    3068 return Outlayer;
    3069 
    3070 }));
    3071 
    3072 
    3073 /**
    3074  * Rect
    3075  * low-level utility class for basic geometry
    3076  */
    3077 
    3078 ( function( window, factory ) {
    3079   
    3080   // universal module definition
    3081   if ( typeof define == 'function' && define.amd ) {
    3082     // AMD
    3083     define( 'packery/js/rect',factory );
    3084   } else if ( typeof exports == 'object' ) {
    3085     // CommonJS
    3086     module.exports = factory();
    3087   } else {
    3088     // browser global
    3089     window.Packery = window.Packery || {};
    3090     window.Packery.Rect = factory();
    3091   }
    3092 
    3093 }( window, function factory() {
    3094 
    3095 
    3096 // -------------------------- Packery -------------------------- //
    3097 
    3098 // global namespace
    3099 var Packery = window.Packery = function() {};
    3100 
    3101 // -------------------------- Rect -------------------------- //
    3102 
    3103 function Rect( props ) {
    3104   // extend properties from defaults
    3105   for ( var prop in Rect.defaults ) {
    3106     this[ prop ] = Rect.defaults[ prop ];
    3107   }
    3108 
    3109   for ( prop in props ) {
    3110     this[ prop ] = props[ prop ];
    3111   }
    3112 
    3113 }
    3114 
    3115 // make available
    3116 Packery.Rect = Rect;
    3117 
    3118 Rect.defaults = {
    3119   x: 0,
    3120   y: 0,
    3121   width: 0,
    3122   height: 0
    3123 };
    3124 
    3125 /**
    3126  * Determines whether or not this rectangle wholly encloses another rectangle or point.
    3127  * @param {Rect} rect
    3128  * @returns {Boolean}
    3129 **/
    3130 Rect.prototype.contains = function( rect ) {
    3131   // points don't have width or height
    3132   var otherWidth = rect.width || 0;
    3133   var otherHeight = rect.height || 0;
    3134   return this.x <= rect.x &&
    3135     this.y <= rect.y &&
    3136     this.x + this.width >= rect.x + otherWidth &&
    3137     this.y + this.height >= rect.y + otherHeight;
    3138 };
    3139 
    3140 /**
    3141  * Determines whether or not the rectangle intersects with another.
    3142  * @param {Rect} rect
    3143  * @returns {Boolean}
    3144 **/
    3145 Rect.prototype.overlaps = function( rect ) {
    3146   var thisRight = this.x + this.width;
    3147   var thisBottom = this.y + this.height;
    3148   var rectRight = rect.x + rect.width;
    3149   var rectBottom = rect.y + rect.height;
    3150 
    3151   // http://stackoverflow.com/a/306332
    3152   return this.x < rectRight &&
    3153     thisRight > rect.x &&
    3154     this.y < rectBottom &&
    3155     thisBottom > rect.y;
    3156 };
    3157 
    3158 /**
    3159  * @param {Rect} rect - the overlapping rect
    3160  * @returns {Array} freeRects - rects representing the area around the rect
    3161 **/
    3162 Rect.prototype.getMaximalFreeRects = function( rect ) {
    3163 
    3164   // if no intersection, return false
    3165   if ( !this.overlaps( rect ) ) {
    3166     return false;
    3167   }
    3168 
    3169   var freeRects = [];
    3170   var freeRect;
    3171 
    3172   var thisRight = this.x + this.width;
    3173   var thisBottom = this.y + this.height;
    3174   var rectRight = rect.x + rect.width;
    3175   var rectBottom = rect.y + rect.height;
    3176 
    3177   // top
    3178   if ( this.y < rect.y ) {
    3179     freeRect = new Rect({
    3180       x: this.x,
    3181       y: this.y,
    3182       width: this.width,
    3183       height: rect.y - this.y
    3184     });
    3185     freeRects.push( freeRect );
    3186   }
    3187 
    3188   // right
    3189   if ( thisRight > rectRight ) {
    3190     freeRect = new Rect({
    3191       x: rectRight,
    3192       y: this.y,
    3193       width: thisRight - rectRight,
    3194       height: this.height
    3195     });
    3196     freeRects.push( freeRect );
    3197   }
    3198 
    3199   // bottom
    3200   if ( thisBottom > rectBottom ) {
    3201     freeRect = new Rect({
    3202       x: this.x,
    3203       y: rectBottom,
    3204       width: this.width,
    3205       height: thisBottom - rectBottom
    3206     });
    3207     freeRects.push( freeRect );
    3208   }
    3209 
    3210   // left
    3211   if ( this.x < rect.x ) {
    3212     freeRect = new Rect({
    3213       x: this.x,
    3214       y: this.y,
    3215       width: rect.x - this.x,
    3216       height: this.height
    3217     });
    3218     freeRects.push( freeRect );
    3219   }
    3220 
    3221   return freeRects;
    3222 };
    3223 
    3224 Rect.prototype.canFit = function( rect ) {
    3225   return this.width >= rect.width && this.height >= rect.height;
    3226 };
    3227 
    3228 return Rect;
    3229 
    3230 }));
    3231 
    3232 /**
    3233  * Packer
    3234  * bin-packing algorithm
    3235  */
    3236 
    3237 ( function( window, factory ) {
    3238   
    3239   // universal module definition
    3240   if ( typeof define == 'function' && define.amd ) {
    3241     // AMD
    3242     define( 'packery/js/packer',[ './rect' ], factory );
    3243   } else if ( typeof exports == 'object' ) {
    3244     // CommonJS
    3245     module.exports = factory(
    3246       require('./rect')
    3247     );
    3248   } else {
    3249     // browser global
    3250     var Packery = window.Packery = window.Packery || {};
    3251     Packery.Packer = factory( Packery.Rect );
    3252   }
    3253 
    3254 }( window, function factory( Rect ) {
    3255 
    3256 
    3257 // -------------------------- Packer -------------------------- //
    3258 
    3259 /**
    3260  * @param {Number} width
    3261  * @param {Number} height
    3262  * @param {String} sortDirection
    3263  *   topLeft for vertical, leftTop for horizontal
    3264  */
    3265 function Packer( width, height, sortDirection ) {
    3266   this.width = width || 0;
    3267   this.height = height || 0;
    3268   this.sortDirection = sortDirection || 'downwardLeftToRight';
    3269 
    3270   this.reset();
    3271 }
    3272 
    3273 Packer.prototype.reset = function() {
    3274   this.spaces = [];
    3275   this.newSpaces = [];
    3276 
    3277   var initialSpace = new Rect({
    3278     x: 0,
    3279     y: 0,
    3280     width: this.width,
    3281     height: this.height
    3282   });
    3283 
    3284   this.spaces.push( initialSpace );
    3285   // set sorter
    3286   this.sorter = sorters[ this.sortDirection ] || sorters.downwardLeftToRight;
    3287 };
    3288 
    3289 // change x and y of rect to fit with in Packer's available spaces
    3290 Packer.prototype.pack = function( rect ) {
    3291   for ( var i=0, len = this.spaces.length; i < len; i++ ) {
    3292     var space = this.spaces[i];
    3293     if ( space.canFit( rect ) ) {
    3294       this.placeInSpace( rect, space );
    3295       break;
    3296     }
    3297   }
    3298 };
    3299 
    3300 Packer.prototype.placeInSpace = function( rect, space ) {
    3301   // place rect in space
    3302   rect.x = space.x;
    3303   rect.y = space.y;
    3304 
    3305   this.placed( rect );
    3306 };
    3307 
    3308 // update spaces with placed rect
    3309 Packer.prototype.placed = function( rect ) {
    3310   // update spaces
    3311   var revisedSpaces = [];
    3312   for ( var i=0, len = this.spaces.length; i < len; i++ ) {
    3313     var space = this.spaces[i];
    3314     var newSpaces = space.getMaximalFreeRects( rect );
    3315     // add either the original space or the new spaces to the revised spaces
    3316     if ( newSpaces ) {
    3317       revisedSpaces.push.apply( revisedSpaces, newSpaces );
    3318     } else {
    3319       revisedSpaces.push( space );
    3320     }
    3321   }
    3322 
    3323   this.spaces = revisedSpaces;
    3324 
    3325   this.mergeSortSpaces();
    3326 };
    3327 
    3328 Packer.prototype.mergeSortSpaces = function() {
    3329   // remove redundant spaces
    3330   Packer.mergeRects( this.spaces );
    3331   this.spaces.sort( this.sorter );
    3332 };
    3333 
    3334 // add a space back
    3335 Packer.prototype.addSpace = function( rect ) {
    3336   this.spaces.push( rect );
    3337   this.mergeSortSpaces();
    3338 };
    3339 
    3340 // -------------------------- utility functions -------------------------- //
    3341 
    3342 /**
    3343  * Remove redundant rectangle from array of rectangles
    3344  * @param {Array} rects: an array of Rects
    3345  * @returns {Array} rects: an array of Rects
    3346 **/
    3347 Packer.mergeRects = function( rects ) {
    3348   for ( var i=0, len = rects.length; i < len; i++ ) {
    3349     var rect = rects[i];
    3350     // skip over this rect if it was already removed
    3351     if ( !rect ) {
    3352       continue;
    3353     }
    3354     // clone rects we're testing, remove this rect
    3355     var compareRects = rects.slice(0);
    3356     // do not compare with self
    3357     compareRects.splice( i, 1 );
    3358     // compare this rect with others
    3359     var removedCount = 0;
    3360     for ( var j=0, jLen = compareRects.length; j < jLen; j++ ) {
    3361       var compareRect = compareRects[j];
    3362       // if this rect contains another,
    3363       // remove that rect from test collection
    3364       var indexAdjust = i > j ? 0 : 1;
    3365       if ( rect.contains( compareRect ) ) {
    3366         // console.log( 'current test rects:' + testRects.length, testRects );
    3367         // console.log( i, j, indexAdjust, rect, compareRect );
    3368         rects.splice( j + indexAdjust - removedCount, 1 );
    3369         removedCount++;
    3370       }
    3371     }
    3372   }
    3373 
    3374   return rects;
    3375 };
    3376 
    3377 
    3378 // -------------------------- sorters -------------------------- //
    3379 
    3380 // functions for sorting rects in order
    3381 var sorters = {
    3382   // top down, then left to right
    3383   downwardLeftToRight: function( a, b ) {
    3384     return a.y - b.y || a.x - b.x;
    3385   },
    3386   // left to right, then top down
    3387   rightwardTopToBottom: function( a, b ) {
    3388     return a.x - b.x || a.y - b.y;
    3389   }
    3390 };
    3391 
    3392 
    3393 // --------------------------  -------------------------- //
    3394 
    3395 return Packer;
    3396 
    3397 }));
    3398 /**
    3399  * Packery Item Element
    3400 **/
    3401 
    3402 ( function( window, factory ) {
    3403   
    3404   // universal module definition
    3405 
    3406   if ( typeof define == 'function' && define.amd ) {
    3407     // AMD
    3408     define( 'packery/js/item',[
    3409         'get-style-property/get-style-property',
    3410         'outlayer/outlayer',
    3411         './rect'
    3412       ],
    3413       factory );
    3414   } else if ( typeof exports == 'object' ) {
    3415     // CommonJS
    3416     module.exports = factory(
    3417       require('desandro-get-style-property'),
    3418       require('outlayer'),
    3419       require('./rect')
    3420     );
    3421   } else {
    3422     // browser global
    3423     window.Packery.Item = factory(
    3424       window.getStyleProperty,
    3425       window.Outlayer,
    3426       window.Packery.Rect
    3427     );
    3428   }
    3429 
    3430 }( window, function factory( getStyleProperty, Outlayer, Rect ) {
    3431 
    3432 
    3433 // -------------------------- Item -------------------------- //
    3434 
    3435 var transformProperty = getStyleProperty('transform');
    3436 
    3437 // sub-class Item
    3438 var Item = function PackeryItem() {
    3439   Outlayer.Item.apply( this, arguments );
    3440 };
    3441 
    3442 Item.prototype = new Outlayer.Item();
    3443 
    3444 var protoCreate = Item.prototype._create;
    3445 Item.prototype._create = function() {
    3446   // call default _create logic
    3447   protoCreate.call( this );
    3448   this.rect = new Rect();
    3449   // rect used for placing, in drag or Packery.fit()
    3450   this.placeRect = new Rect();
    3451 };
    3452 
    3453 // -------------------------- drag -------------------------- //
    3454 
    3455 Item.prototype.dragStart = function() {
    3456   this.getPosition();
    3457   this.removeTransitionStyles();
    3458   // remove transform property from transition
    3459   if ( this.isTransitioning && transformProperty ) {
    3460     this.element.style[ transformProperty ] = 'none';
    3461   }
    3462   this.getSize();
    3463   // create place rect, used for position when dragged then dropped
    3464   // or when positioning
    3465   this.isPlacing = true;
    3466   this.needsPositioning = false;
    3467   this.positionPlaceRect( this.position.x, this.position.y );
    3468   this.isTransitioning = false;
    3469   this.didDrag = false;
    3470 };
    3471 
    3472 /**
    3473  * handle item when it is dragged
    3474  * @param {Number} x - horizontal position of dragged item
    3475  * @param {Number} y - vertical position of dragged item
    3476  */
    3477 Item.prototype.dragMove = function( x, y ) {
    3478   this.didDrag = true;
    3479   var packerySize = this.layout.size;
    3480   x -= packerySize.paddingLeft;
    3481   y -= packerySize.paddingTop;
    3482   this.positionPlaceRect( x, y );
    3483 };
    3484 
    3485 Item.prototype.dragStop = function() {
    3486   this.getPosition();
    3487   var isDiffX = this.position.x != this.placeRect.x;
    3488   var isDiffY = this.position.y != this.placeRect.y;
    3489   // set post-drag positioning flag
    3490   this.needsPositioning = isDiffX || isDiffY;
    3491   // reset flag
    3492   this.didDrag = false;
    3493 };
    3494 
    3495 // -------------------------- placing -------------------------- //
    3496 
    3497 /**
    3498  * position a rect that will occupy space in the packer
    3499  * @param {Number} x
    3500  * @param {Number} y
    3501  * @param {Boolean} isMaxYContained
    3502  */
    3503 Item.prototype.positionPlaceRect = function( x, y, isMaxYOpen ) {
    3504   this.placeRect.x = this.getPlaceRectCoord( x, true );
    3505   this.placeRect.y = this.getPlaceRectCoord( y, false, isMaxYOpen );
    3506 };
    3507 
    3508 /**
    3509  * get x/y coordinate for place rect
    3510  * @param {Number} coord - x or y
    3511  * @param {Boolean} isX
    3512  * @param {Boolean} isMaxOpen - does not limit value to outer bound
    3513  * @returns {Number} coord - processed x or y
    3514  */
    3515 Item.prototype.getPlaceRectCoord = function( coord, isX, isMaxOpen ) {
    3516   var measure = isX ? 'Width' : 'Height';
    3517   var size = this.size[ 'outer' + measure ];
    3518   var segment = this.layout[ isX ? 'columnWidth' : 'rowHeight' ];
    3519   var parentSize = this.layout.size[ 'inner' + measure ];
    3520 
    3521   // additional parentSize calculations for Y
    3522   if ( !isX ) {
    3523     parentSize = Math.max( parentSize, this.layout.maxY );
    3524     // prevent gutter from bumping up height when non-vertical grid
    3525     if ( !this.layout.rowHeight ) {
    3526       parentSize -= this.layout.gutter;
    3527     }
    3528   }
    3529 
    3530   var max;
    3531 
    3532   if ( segment ) {
    3533     segment += this.layout.gutter;
    3534     // allow for last column to reach the edge
    3535     parentSize += isX ? this.layout.gutter : 0;
    3536     // snap to closest segment
    3537     coord = Math.round( coord / segment );
    3538     // contain to outer bound
    3539     // contain non-growing bound, allow growing bound to grow
    3540     var mathMethod;
    3541     if ( this.layout.options.isHorizontal ) {
    3542       mathMethod = !isX ? 'floor' : 'ceil';
    3543     } else {
    3544       mathMethod = isX ? 'floor' : 'ceil';
    3545     }
    3546     var maxSegments = Math[ mathMethod ]( parentSize / segment );
    3547     maxSegments -= Math.ceil( size / segment );
    3548     max = maxSegments;
    3549   } else {
    3550     max = parentSize - size;
    3551   }
    3552 
    3553   coord = isMaxOpen ? coord : Math.min( coord, max );
    3554   coord *= segment || 1;
    3555 
    3556   return Math.max( 0, coord );
    3557 };
    3558 
    3559 Item.prototype.copyPlaceRectPosition = function() {
    3560   this.rect.x = this.placeRect.x;
    3561   this.rect.y = this.placeRect.y;
    3562 };
    3563 
    3564 // -----  ----- //
    3565 
    3566 // remove element from DOM
    3567 Item.prototype.removeElem = function() {
    3568   this.element.parentNode.removeChild( this.element );
    3569   // add space back to packer
    3570   this.layout.packer.addSpace( this.rect );
    3571   this.emitEvent( 'remove', [ this ] );
    3572 };
    3573 
    3574 // -----  ----- //
    3575 
    3576 return Item;
    3577 
    3578 }));
    3579 
    3580 /*!
    3581  * Packery v1.4.3
    3582  * bin-packing layout library
    3583  *
    3584  * Licensed GPLv3 for open source use
    3585  * or Flickity Commercial License for commercial use
    3586  *
    3587  * http://packery.metafizzy.co
    3588  * Copyright 2015 Metafizzy
    3589  */
    3590 
    3591 ( function( window, factory ) {
    3592   
    3593   // universal module definition
    3594   if ( typeof define == 'function' && define.amd ) {
    3595     // AMD
    3596     define( [
    3597         'classie/classie',
    3598         'get-size/get-size',
    3599         'outlayer/outlayer',
    3600         'packery/js/rect',
    3601         'packery/js/packer',
    3602         'packery/js/item'
    3603       ],
    3604       factory );
    3605   } else if ( typeof exports == 'object' ) {
    3606     // CommonJS
    3607     module.exports = factory(
    3608       require('desandro-classie'),
    3609       require('get-size'),
    3610       require('outlayer'),
    3611       require('./rect'),
    3612       require('./packer'),
    3613       require('./item')
    3614     );
    3615   } else {
    3616     // browser global
    3617     window.Packery = factory(
    3618       window.classie,
    3619       window.getSize,
    3620       window.Outlayer,
    3621       window.Packery.Rect,
    3622       window.Packery.Packer,
    3623       window.Packery.Item
    3624     );
    3625   }
    3626 
    3627 }( window, function factory( classie, getSize, Outlayer, Rect, Packer, Item ) {
    3628 
    3629 
    3630 // ----- Rect ----- //
    3631 
    3632 // allow for pixel rounding errors IE8-IE11 & Firefox; #227
    3633 Rect.prototype.canFit = function( rect ) {
    3634   return this.width >= rect.width - 1 && this.height >= rect.height - 1;
    3635 };
    3636 
    3637 // -------------------------- Packery -------------------------- //
    3638 
    3639 // create an Outlayer layout class
    3640 var Packery = Outlayer.create('packery');
    3641 Packery.Item = Item;
    3642 
    3643 Packery.prototype._create = function() {
    3644   // call super
    3645   Outlayer.prototype._create.call( this );
    3646 
    3647   // initial properties
    3648   this.packer = new Packer();
    3649 
    3650   // Left over from v1.0
    3651   this.stamp( this.options.stamped );
    3652 
    3653   // create drag handlers
    3654   var _this = this;
    3655   this.handleDraggabilly = {
    3656     dragStart: function() {
    3657       _this.itemDragStart( this.element );
    3658     },
    3659     dragMove: function() {
    3660       _this.itemDragMove( this.element, this.position.x, this.position.y );
    3661     },
    3662     dragEnd: function() {
    3663       _this.itemDragEnd( this.element );
    3664     }
    3665   };
    3666 
    3667   this.handleUIDraggable = {
    3668     start: function handleUIDraggableStart( event, ui ) {
    3669       // HTML5 may trigger dragstart, dismiss HTML5 dragging
    3670       if ( !ui ) {
    3671         return;
    3672       }
    3673       _this.itemDragStart( event.currentTarget );
    3674     },
    3675     drag: function handleUIDraggableDrag( event, ui ) {
    3676       if ( !ui ) {
    3677         return;
    3678       }
    3679       _this.itemDragMove( event.currentTarget, ui.position.left, ui.position.top );
    3680     },
    3681     stop: function handleUIDraggableStop( event, ui ) {
    3682       if ( !ui ) {
    3683         return;
    3684       }
    3685       _this.itemDragEnd( event.currentTarget );
    3686     }
    3687   };
    3688 
    3689 };
    3690 
    3691 
    3692 // ----- init & layout ----- //
    3693 
    3694 /**
    3695  * logic before any new layout
    3696  */
    3697 Packery.prototype._resetLayout = function() {
    3698   this.getSize();
    3699 
    3700   this._getMeasurements();
    3701 
    3702   // reset packer
    3703   var packer = this.packer;
    3704   // packer settings, if horizontal or vertical
    3705   if ( this.options.isHorizontal ) {
    3706     packer.width = Number.POSITIVE_INFINITY;
    3707     packer.height = this.size.innerHeight + this.gutter;
    3708     packer.sortDirection = 'rightwardTopToBottom';
    3709   } else {
    3710     packer.width = this.size.innerWidth + this.gutter;
    3711     packer.height = Number.POSITIVE_INFINITY;
    3712     packer.sortDirection = 'downwardLeftToRight';
    3713   }
    3714 
    3715   packer.reset();
    3716 
    3717   // layout
    3718   this.maxY = 0;
    3719   this.maxX = 0;
    3720 };
    3721 
    3722 /**
    3723  * update columnWidth, rowHeight, & gutter
    3724  * @private
    3725  */
    3726 Packery.prototype._getMeasurements = function() {
    3727   this._getMeasurement( 'columnWidth', 'width' );
    3728   this._getMeasurement( 'rowHeight', 'height' );
    3729   this._getMeasurement( 'gutter', 'width' );
    3730 };
    3731 
    3732 Packery.prototype._getItemLayoutPosition = function( item ) {
    3733   this._packItem( item );
    3734   return item.rect;
    3735 };
    3736 
    3737 
    3738 /**
    3739  * layout item in packer
    3740  * @param {Packery.Item} item
    3741  */
    3742 Packery.prototype._packItem = function( item ) {
    3743   this._setRectSize( item.element, item.rect );
    3744   // pack the rect in the packer
    3745   this.packer.pack( item.rect );
    3746   this._setMaxXY( item.rect );
    3747 };
    3748 
    3749 /**
    3750  * set max X and Y value, for size of container
    3751  * @param {Packery.Rect} rect
    3752  * @private
    3753  */
    3754 Packery.prototype._setMaxXY = function( rect ) {
    3755   this.maxX = Math.max( rect.x + rect.width, this.maxX );
    3756   this.maxY = Math.max( rect.y + rect.height, this.maxY );
    3757 };
    3758 
    3759 /**
    3760  * set the width and height of a rect, applying columnWidth and rowHeight
    3761  * @param {Element} elem
    3762  * @param {Packery.Rect} rect
    3763  */
    3764 Packery.prototype._setRectSize = function( elem, rect ) {
    3765   var size = getSize( elem );
    3766   var w = size.outerWidth;
    3767   var h = size.outerHeight;
    3768   // size for columnWidth and rowHeight, if available
    3769   // only check if size is non-zero, #177
    3770   if ( w || h ) {
    3771     w = this._applyGridGutter( w, this.columnWidth );
    3772     h = this._applyGridGutter( h, this.rowHeight );
    3773   }
    3774   // rect must fit in packer
    3775   rect.width = Math.min( w, this.packer.width );
    3776   rect.height = Math.min( h, this.packer.height );
    3777 };
    3778 
    3779 /**
    3780  * fits item to columnWidth/rowHeight and adds gutter
    3781  * @param {Number} measurement - item width or height
    3782  * @param {Number} gridSize - columnWidth or rowHeight
    3783  * @returns measurement
    3784  */
    3785 Packery.prototype._applyGridGutter = function( measurement, gridSize ) {
    3786   // just add gutter if no gridSize
    3787   if ( !gridSize ) {
    3788     return measurement + this.gutter;
    3789   }
    3790   gridSize += this.gutter;
    3791   // fit item to columnWidth/rowHeight
    3792   var remainder = measurement % gridSize;
    3793   var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
    3794   measurement = Math[ mathMethod ]( measurement / gridSize ) * gridSize;
    3795   return measurement;
    3796 };
    3797 
    3798 Packery.prototype._getContainerSize = function() {
    3799   if ( this.options.isHorizontal ) {
    3800     return {
    3801       width: this.maxX - this.gutter
    3802     };
    3803   } else {
    3804     return {
    3805       height: this.maxY - this.gutter
    3806     };
    3807   }
    3808 };
    3809 
    3810 
    3811 // -------------------------- stamp -------------------------- //
    3812 
    3813 /**
    3814  * makes space for element
    3815  * @param {Element} elem
    3816  */
    3817 Packery.prototype._manageStamp = function( elem ) {
    3818 
    3819   var item = this.getItem( elem );
    3820   var rect;
    3821   if ( item && item.isPlacing ) {
    3822     rect = item.placeRect;
    3823   } else {
    3824     var offset = this._getElementOffset( elem );
    3825     rect = new Rect({
    3826       x: this.options.isOriginLeft ? offset.left : offset.right,
    3827       y: this.options.isOriginTop ? offset.top : offset.bottom
    3828     });
    3829   }
    3830 
    3831   this._setRectSize( elem, rect );
    3832   // save its space in the packer
    3833   this.packer.placed( rect );
    3834   this._setMaxXY( rect );
    3835 };
    3836 
    3837 // -------------------------- methods -------------------------- //
    3838 
    3839 function verticalSorter( a, b ) {
    3840   return a.position.y - b.position.y || a.position.x - b.position.x;
    3841 }
    3842 
    3843 function horizontalSorter( a, b ) {
    3844   return a.position.x - b.position.x || a.position.y - b.position.y;
    3845 }
    3846 
    3847 Packery.prototype.sortItemsByPosition = function() {
    3848   var sorter = this.options.isHorizontal ? horizontalSorter : verticalSorter;
    3849   this.items.sort( sorter );
    3850 };
    3851 
    3852 /**
    3853  * Fit item element in its current position
    3854  * Packery will position elements around it
    3855  * useful for expanding elements
    3856  *
    3857  * @param {Element} elem
    3858  * @param {Number} x - horizontal destination position, optional
    3859  * @param {Number} y - vertical destination position, optional
    3860  */
    3861 Packery.prototype.fit = function( elem, x, y ) {
    3862   var item = this.getItem( elem );
    3863   if ( !item ) {
    3864     return;
    3865   }
    3866 
    3867   // prepare internal properties
    3868   this._getMeasurements();
    3869 
    3870   // stamp item to get it out of layout
    3871   this.stamp( item.element );
    3872   // required for positionPlaceRect
    3873   item.getSize();
    3874   // set placing flag
    3875   item.isPlacing = true;
    3876   // fall back to current position for fitting
    3877   x = x === undefined ? item.rect.x: x;
    3878   y = y === undefined ? item.rect.y: y;
    3879 
    3880   // position it best at its destination
    3881   item.positionPlaceRect( x, y, true );
    3882 
    3883   this._bindFitEvents( item );
    3884   item.moveTo( item.placeRect.x, item.placeRect.y );
    3885   // layout everything else
    3886   this.layout();
    3887 
    3888   // return back to regularly scheduled programming
    3889   this.unstamp( item.element );
    3890   this.sortItemsByPosition();
    3891   // un set placing flag, back to normal
    3892   item.isPlacing = false;
    3893   // copy place rect position
    3894   item.copyPlaceRectPosition();
    3895 };
    3896 
    3897 /**
    3898  * emit event when item is fit and other items are laid out
    3899  * @param {Packery.Item} item
    3900  * @private
    3901  */
    3902 Packery.prototype._bindFitEvents = function( item ) {
    3903   var _this = this;
    3904   var ticks = 0;
    3905   function tick() {
    3906     ticks++;
    3907     if ( ticks != 2 ) {
    3908       return;
    3909     }
    3910     _this.dispatchEvent( 'fitComplete', null, [ item ] );
    3911   }
    3912   // when item is laid out
    3913   item.on( 'layout', function() {
    3914     tick();
    3915     return true;
    3916   });
    3917   // when all items are laid out
    3918   this.on( 'layoutComplete', function() {
    3919     tick();
    3920     return true;
    3921   });
    3922 };
    3923 
    3924 // -------------------------- resize -------------------------- //
    3925 
    3926 // debounced, layout on resize
    3927 Packery.prototype.resize = function() {
    3928   // don't trigger if size did not change
    3929   var size = getSize( this.element );
    3930   // check that this.size and size are there
    3931   // IE8 triggers resize on body size change, so they might not be
    3932   var hasSizes = this.size && size;
    3933   var innerSize = this.options.isHorizontal ? 'innerHeight' : 'innerWidth';
    3934   if ( hasSizes && size[ innerSize ] == this.size[ innerSize ] ) {
    3935     return;
    3936   }
    3937 
    3938   this.layout();
    3939 };
    3940 
    3941 // -------------------------- drag -------------------------- //
    3942 
    3943 /**
    3944  * handle an item drag start event
    3945  * @param {Element} elem
    3946  */
    3947 Packery.prototype.itemDragStart = function( elem ) {
    3948   this.stamp( elem );
    3949   var item = this.getItem( elem );
    3950   if ( item ) {
    3951     item.dragStart();
    3952   }
    3953 };
    3954 
    3955 /**
    3956  * handle an item drag move event
    3957  * @param {Element} elem
    3958  * @param {Number} x - horizontal change in position
    3959  * @param {Number} y - vertical change in position
    3960  */
    3961 Packery.prototype.itemDragMove = function( elem, x, y ) {
    3962   var item = this.getItem( elem );
    3963   if ( item ) {
    3964     item.dragMove( x, y );
    3965   }
    3966 
    3967   // debounce
    3968   var _this = this;
    3969   // debounce triggering layout
    3970   function delayed() {
    3971     _this.layout();
    3972     delete _this.dragTimeout;
    3973   }
    3974 
    3975   this.clearDragTimeout();
    3976 
    3977   this.dragTimeout = setTimeout( delayed, 40 );
    3978 };
    3979 
    3980 Packery.prototype.clearDragTimeout = function() {
    3981   if ( this.dragTimeout ) {
    3982     clearTimeout( this.dragTimeout );
    3983   }
    3984 };
    3985 
    3986 /**
    3987  * handle an item drag end event
    3988  * @param {Element} elem
    3989  */
    3990 Packery.prototype.itemDragEnd = function( elem ) {
    3991   var item = this.getItem( elem );
    3992   var itemDidDrag;
    3993   if ( item ) {
    3994     itemDidDrag = item.didDrag;
    3995     item.dragStop();
    3996   }
    3997   // if elem didn't move, or if it doesn't need positioning
    3998   // unignore and unstamp and call it a day
    3999   if ( !item || ( !itemDidDrag && !item.needsPositioning ) ) {
    4000     this.unstamp( elem );
    4001     return;
    4002   }
    4003   // procced with dragged item
    4004 
    4005   classie.add( item.element, 'is-positioning-post-drag' );
    4006 
    4007   // save this var, as it could get reset in dragStart
    4008   var onLayoutComplete = this._getDragEndLayoutComplete( elem, item );
    4009 
    4010   if ( item.needsPositioning ) {
    4011     item.on( 'layout', onLayoutComplete );
    4012     item.moveTo( item.placeRect.x, item.placeRect.y );
    4013   } else if ( item ) {
    4014     // item didn't need placement
    4015     item.copyPlaceRectPosition();
    4016   }
    4017 
    4018   this.clearDragTimeout();
    4019   this.on( 'layoutComplete', onLayoutComplete );
    4020   this.layout();
    4021 
    4022 };
    4023 
    4024 /**
    4025  * get drag end callback
    4026  * @param {Element} elem
    4027  * @param {Packery.Item} item
    4028  * @returns {Function} onLayoutComplete
    4029  */
    4030 Packery.prototype._getDragEndLayoutComplete = function( elem, item ) {
    4031   var itemNeedsPositioning = item && item.needsPositioning;
    4032   var completeCount = 0;
    4033   var asyncCount = itemNeedsPositioning ? 2 : 1;
    4034   var _this = this;
    4035 
    4036   return function onLayoutComplete() {
    4037     completeCount++;
    4038     // don't proceed if not complete
    4039     if ( completeCount != asyncCount ) {
    4040       return true;
    4041     }
    4042     // reset item
    4043     if ( item ) {
    4044       classie.remove( item.element, 'is-positioning-post-drag' );
    4045       item.isPlacing = false;
    4046       item.copyPlaceRectPosition();
    4047     }
    4048 
    4049     _this.unstamp( elem );
    4050     // only sort when item moved
    4051     _this.sortItemsByPosition();
    4052 
    4053     // emit item drag event now that everything is done
    4054     if ( itemNeedsPositioning ) {
    4055       _this.dispatchEvent( 'dragItemPositioned', null, [ item ] );
    4056     }
    4057     // listen once
    4058     return true;
    4059   };
    4060 };
    4061 
    4062 /**
    4063  * binds Draggabilly events
    4064  * @param {Draggabilly} draggie
    4065  */
    4066 Packery.prototype.bindDraggabillyEvents = function( draggie ) {
    4067   draggie.on( 'dragStart', this.handleDraggabilly.dragStart );
    4068   draggie.on( 'dragMove', this.handleDraggabilly.dragMove );
    4069   draggie.on( 'dragEnd', this.handleDraggabilly.dragEnd );
    4070 };
    4071 
    4072 /**
    4073  * binds jQuery UI Draggable events
    4074  * @param {jQuery} $elems
    4075  */
    4076 Packery.prototype.bindUIDraggableEvents = function( $elems ) {
    4077   $elems
    4078     .on( 'dragstart', this.handleUIDraggable.start )
    4079     .on( 'drag', this.handleUIDraggable.drag )
    4080     .on( 'dragstop', this.handleUIDraggable.stop );
    4081 };
    4082 
    4083 Packery.Rect = Rect;
    4084 Packery.Packer = Packer;
    4085 
    4086 return Packery;
    4087 
    4088 }));
    4089